@mauroandre/weave-sdk 0.0.1
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.
- package/dist/chunk-PWTED7ZO.js +767 -0
- package/dist/chunk-PWTED7ZO.js.map +1 -0
- package/dist/cli.d.ts +50 -0
- package/dist/cli.js +169 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +148 -0
- package/dist/index.js +189 -0
- package/dist/index.js.map +1 -0
- package/dist/scope-DrcCzdf-.d.ts +100 -0
- package/package.json +47 -0
|
@@ -0,0 +1,767 @@
|
|
|
1
|
+
// ../core/src/schema/column.ts
|
|
2
|
+
var Column = class _Column {
|
|
3
|
+
constructor(config) {
|
|
4
|
+
this.config = config;
|
|
5
|
+
}
|
|
6
|
+
config;
|
|
7
|
+
/** Mark the column `NOT NULL` — narrows the read type from `T | null` to `T`. */
|
|
8
|
+
notNull() {
|
|
9
|
+
return new _Column({ ...this.config, notNull: true });
|
|
10
|
+
}
|
|
11
|
+
/** Mark the column nullable — widens the read type back to `T | null`. */
|
|
12
|
+
nullable() {
|
|
13
|
+
return new _Column({ ...this.config, notNull: false });
|
|
14
|
+
}
|
|
15
|
+
/** Declare a default value — makes the column optional on insert. */
|
|
16
|
+
default(value) {
|
|
17
|
+
return new _Column({ ...this.config, hasDefault: true, default: value });
|
|
18
|
+
}
|
|
19
|
+
/** Add a single-column `UNIQUE`. */
|
|
20
|
+
unique() {
|
|
21
|
+
return new _Column({ ...this.config, unique: true });
|
|
22
|
+
}
|
|
23
|
+
/** Add a single-column index. */
|
|
24
|
+
index() {
|
|
25
|
+
return new _Column({ ...this.config, index: true });
|
|
26
|
+
}
|
|
27
|
+
/** Pin a stable field id (survives rename). Normally emitted by `weave gen`. */
|
|
28
|
+
$id(id) {
|
|
29
|
+
return new _Column({ ...this.config, id });
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
function scalarColumn(pgType) {
|
|
33
|
+
return new Column({
|
|
34
|
+
pgType,
|
|
35
|
+
isArray: false,
|
|
36
|
+
notNull: false,
|
|
37
|
+
hasDefault: false,
|
|
38
|
+
unique: false,
|
|
39
|
+
index: false
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ../core/src/schema/owned.ts
|
|
44
|
+
var Owned = class _Owned {
|
|
45
|
+
constructor(shape, cardinality, options, id) {
|
|
46
|
+
this.shape = shape;
|
|
47
|
+
this.cardinality = cardinality;
|
|
48
|
+
this.options = options;
|
|
49
|
+
this.id = id;
|
|
50
|
+
}
|
|
51
|
+
shape;
|
|
52
|
+
cardinality;
|
|
53
|
+
options;
|
|
54
|
+
id;
|
|
55
|
+
kind = "owned";
|
|
56
|
+
/** Pin a stable field id (survives rename). Normally emitted by `weave gen`. */
|
|
57
|
+
$id(id) {
|
|
58
|
+
return new _Owned(this.shape, this.cardinality, this.options, id);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
var OwnedArray = class {
|
|
62
|
+
constructor(shape) {
|
|
63
|
+
this.shape = shape;
|
|
64
|
+
}
|
|
65
|
+
shape;
|
|
66
|
+
kind = "owned_array";
|
|
67
|
+
};
|
|
68
|
+
function owned(arg, options = {}) {
|
|
69
|
+
if (arg instanceof OwnedArray) {
|
|
70
|
+
return new Owned(arg.shape, "many", options);
|
|
71
|
+
}
|
|
72
|
+
return new Owned(arg, "one", options);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ../core/src/schema/reference.ts
|
|
76
|
+
var Reference = class _Reference {
|
|
77
|
+
constructor(target, cardinality, isNotNull, id) {
|
|
78
|
+
this.target = target;
|
|
79
|
+
this.cardinality = cardinality;
|
|
80
|
+
this.isNotNull = isNotNull;
|
|
81
|
+
this.id = id;
|
|
82
|
+
}
|
|
83
|
+
target;
|
|
84
|
+
cardinality;
|
|
85
|
+
isNotNull;
|
|
86
|
+
id;
|
|
87
|
+
kind = "reference";
|
|
88
|
+
/** Make the FK `NOT NULL` (only meaningful for N:1). */
|
|
89
|
+
notNull() {
|
|
90
|
+
return new _Reference(this.target, this.cardinality, true, this.id);
|
|
91
|
+
}
|
|
92
|
+
/** Pin a stable field id (survives rename). Normally emitted by `weave gen`. */
|
|
93
|
+
$id(id) {
|
|
94
|
+
return new _Reference(this.target, this.cardinality, this.isNotNull, id);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
var ReferenceArray = class {
|
|
98
|
+
constructor(target) {
|
|
99
|
+
this.target = target;
|
|
100
|
+
}
|
|
101
|
+
target;
|
|
102
|
+
kind = "reference_array";
|
|
103
|
+
};
|
|
104
|
+
function reference(arg) {
|
|
105
|
+
if (arg instanceof ReferenceArray) {
|
|
106
|
+
return new Reference(arg.target, "many", false);
|
|
107
|
+
}
|
|
108
|
+
return new Reference(arg, "one", false);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ../core/src/types/pg-type.ts
|
|
112
|
+
function defineType() {
|
|
113
|
+
return (def) => ({
|
|
114
|
+
...def,
|
|
115
|
+
// Phantom: exists only so the compiler can recover `TTs`. Never read at runtime.
|
|
116
|
+
tsType: void 0
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// ../core/src/types/catalog.ts
|
|
121
|
+
var int2 = defineType()({ name: "int2", sqlType: "smallint", oid: 21, tsLabel: "number" });
|
|
122
|
+
var int4 = defineType()({ name: "int4", sqlType: "integer", oid: 23, tsLabel: "number" });
|
|
123
|
+
var int8 = defineType()({ name: "int8", sqlType: "bigint", oid: 20, tsLabel: "bigint" });
|
|
124
|
+
var numeric = defineType()({ name: "numeric", sqlType: "numeric", oid: 1700, tsLabel: "number" });
|
|
125
|
+
var float4 = defineType()({ name: "float4", sqlType: "real", oid: 700, tsLabel: "number" });
|
|
126
|
+
var float8 = defineType()({ name: "float8", sqlType: "double precision", oid: 701, tsLabel: "number" });
|
|
127
|
+
var text = defineType()({ name: "text", sqlType: "text", oid: 25, tsLabel: "string" });
|
|
128
|
+
var varchar = defineType()({ name: "varchar", sqlType: "varchar", oid: 1043, tsLabel: "string" });
|
|
129
|
+
var bpchar = defineType()({ name: "bpchar", sqlType: "char", oid: 1042, tsLabel: "string" });
|
|
130
|
+
var timestamptz = defineType()({ name: "timestamptz", sqlType: "timestamp with time zone", oid: 1184, tsLabel: "Date" });
|
|
131
|
+
var timestamp = defineType()({ name: "timestamp", sqlType: "timestamp", oid: 1114, tsLabel: "Date" });
|
|
132
|
+
var date = defineType()({ name: "date", sqlType: "date", oid: 1082, tsLabel: "Date" });
|
|
133
|
+
var time = defineType()({ name: "time", sqlType: "time", oid: 1083, tsLabel: "string" });
|
|
134
|
+
var interval = defineType()({ name: "interval", sqlType: "interval", oid: 1186, tsLabel: "string" });
|
|
135
|
+
var bool = defineType()({ name: "bool", sqlType: "boolean", oid: 16, tsLabel: "boolean" });
|
|
136
|
+
var uuid = defineType()({ name: "uuid", sqlType: "uuid", oid: 2950, tsLabel: "string" });
|
|
137
|
+
var json = defineType()({ name: "json", sqlType: "json", oid: 114, tsLabel: "unknown" });
|
|
138
|
+
var jsonb = defineType()({ name: "jsonb", sqlType: "jsonb", oid: 3802, tsLabel: "unknown" });
|
|
139
|
+
var bytea = defineType()({ name: "bytea", sqlType: "bytea", oid: 17, tsLabel: "Uint8Array" });
|
|
140
|
+
|
|
141
|
+
// ../core/src/types/registry.ts
|
|
142
|
+
var catalog = {
|
|
143
|
+
int2,
|
|
144
|
+
int4,
|
|
145
|
+
int8,
|
|
146
|
+
numeric,
|
|
147
|
+
float4,
|
|
148
|
+
float8,
|
|
149
|
+
text,
|
|
150
|
+
varchar,
|
|
151
|
+
bpchar,
|
|
152
|
+
timestamptz,
|
|
153
|
+
timestamp,
|
|
154
|
+
date,
|
|
155
|
+
time,
|
|
156
|
+
interval,
|
|
157
|
+
bool,
|
|
158
|
+
uuid,
|
|
159
|
+
json,
|
|
160
|
+
jsonb,
|
|
161
|
+
bytea
|
|
162
|
+
};
|
|
163
|
+
var allTypes = Object.values(catalog);
|
|
164
|
+
var byName = new Map(
|
|
165
|
+
allTypes.map((t) => [t.name, t])
|
|
166
|
+
);
|
|
167
|
+
var byOid = new Map(
|
|
168
|
+
allTypes.map((t) => [t.oid, t])
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
// ../core/src/schema/scalars.ts
|
|
172
|
+
var int22 = () => scalarColumn(catalog.int2);
|
|
173
|
+
var int42 = () => scalarColumn(catalog.int4);
|
|
174
|
+
var int82 = () => scalarColumn(catalog.int8);
|
|
175
|
+
var numeric2 = () => scalarColumn(catalog.numeric);
|
|
176
|
+
var float42 = () => scalarColumn(catalog.float4);
|
|
177
|
+
var float82 = () => scalarColumn(catalog.float8);
|
|
178
|
+
var text2 = () => scalarColumn(catalog.text);
|
|
179
|
+
var varchar2 = () => scalarColumn(catalog.varchar);
|
|
180
|
+
var bpchar2 = () => scalarColumn(catalog.bpchar);
|
|
181
|
+
var timestamptz2 = () => scalarColumn(catalog.timestamptz);
|
|
182
|
+
var timestamp2 = () => scalarColumn(catalog.timestamp);
|
|
183
|
+
var date2 = () => scalarColumn(catalog.date);
|
|
184
|
+
var time2 = () => scalarColumn(catalog.time);
|
|
185
|
+
var interval2 = () => scalarColumn(catalog.interval);
|
|
186
|
+
var bool2 = () => scalarColumn(catalog.bool);
|
|
187
|
+
var uuid2 = () => scalarColumn(catalog.uuid);
|
|
188
|
+
var json2 = () => scalarColumn(catalog.json);
|
|
189
|
+
var jsonb2 = () => scalarColumn(catalog.jsonb);
|
|
190
|
+
var bytea2 = () => scalarColumn(catalog.bytea);
|
|
191
|
+
function array(arg) {
|
|
192
|
+
if (arg instanceof Column) {
|
|
193
|
+
return new Column({
|
|
194
|
+
pgType: arg.config.pgType,
|
|
195
|
+
isArray: true,
|
|
196
|
+
notNull: true,
|
|
197
|
+
hasDefault: true,
|
|
198
|
+
default: [],
|
|
199
|
+
unique: false,
|
|
200
|
+
index: false
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
const candidate = arg;
|
|
204
|
+
if (typeof candidate.name === "string" && typeof candidate.columns === "object") {
|
|
205
|
+
return new ReferenceArray(arg);
|
|
206
|
+
}
|
|
207
|
+
return new OwnedArray(arg);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// ../core/src/schema/entity.ts
|
|
211
|
+
function defineEntity(name, columns) {
|
|
212
|
+
return { name, columns };
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// ../core/src/ir/to-ir.ts
|
|
216
|
+
function toIR(entity) {
|
|
217
|
+
return { irVersion: 1, name: entity.name, fields: shapeToIR(entity.columns) };
|
|
218
|
+
}
|
|
219
|
+
function shapeToIR(shape) {
|
|
220
|
+
const out = {};
|
|
221
|
+
for (const [key, node] of Object.entries(shape)) out[key] = nodeToIR(node);
|
|
222
|
+
return out;
|
|
223
|
+
}
|
|
224
|
+
function nodeToIR(node) {
|
|
225
|
+
if (node instanceof Column) {
|
|
226
|
+
const c = node.config;
|
|
227
|
+
const ir = { kind: "column", type: c.pgType.name };
|
|
228
|
+
if (c.id) ir.id = c.id;
|
|
229
|
+
if (c.isArray) ir.array = true;
|
|
230
|
+
if (c.notNull) ir.notNull = true;
|
|
231
|
+
if (c.hasDefault) ir.default = c.default;
|
|
232
|
+
if (c.unique) ir.unique = true;
|
|
233
|
+
if (c.index) ir.index = true;
|
|
234
|
+
return ir;
|
|
235
|
+
}
|
|
236
|
+
if (node instanceof Reference) {
|
|
237
|
+
const ir = {
|
|
238
|
+
kind: "reference",
|
|
239
|
+
target: node.target.name,
|
|
240
|
+
cardinality: node.cardinality
|
|
241
|
+
};
|
|
242
|
+
if (node.id) ir.id = node.id;
|
|
243
|
+
if (node.isNotNull) ir.notNull = true;
|
|
244
|
+
return ir;
|
|
245
|
+
}
|
|
246
|
+
if (node instanceof Owned) {
|
|
247
|
+
const ir = {
|
|
248
|
+
kind: "owned",
|
|
249
|
+
array: node.cardinality === "many",
|
|
250
|
+
shape: shapeToIR(node.shape)
|
|
251
|
+
};
|
|
252
|
+
if (node.id) ir.id = node.id;
|
|
253
|
+
if (node.options.table !== void 0) ir.table = node.options.table;
|
|
254
|
+
return ir;
|
|
255
|
+
}
|
|
256
|
+
throw new Error("toIR \u2014 n\xF3 desconhecido no shape (esperado Column/Owned/Reference).");
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// src/errors.ts
|
|
260
|
+
var WeaveError = class extends Error {
|
|
261
|
+
constructor(message, status) {
|
|
262
|
+
super(message);
|
|
263
|
+
this.status = status;
|
|
264
|
+
this.name = "WeaveError";
|
|
265
|
+
}
|
|
266
|
+
status;
|
|
267
|
+
};
|
|
268
|
+
var WeaveAuthError = class extends WeaveError {
|
|
269
|
+
constructor(message) {
|
|
270
|
+
super(message, 401);
|
|
271
|
+
this.name = "WeaveAuthError";
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
var WeaveScopeError = class extends WeaveError {
|
|
275
|
+
constructor(message) {
|
|
276
|
+
super(message, 403);
|
|
277
|
+
this.name = "WeaveScopeError";
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
var WeaveNotFoundError = class extends WeaveError {
|
|
281
|
+
constructor(message) {
|
|
282
|
+
super(message, 404);
|
|
283
|
+
this.name = "WeaveNotFoundError";
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
var WeaveValidationError = class extends WeaveError {
|
|
287
|
+
constructor(message) {
|
|
288
|
+
super(message, 400);
|
|
289
|
+
this.name = "WeaveValidationError";
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
function errorFor(status, message) {
|
|
293
|
+
switch (status) {
|
|
294
|
+
case 401:
|
|
295
|
+
return new WeaveAuthError(message);
|
|
296
|
+
case 403:
|
|
297
|
+
return new WeaveScopeError(message);
|
|
298
|
+
case 404:
|
|
299
|
+
return new WeaveNotFoundError(message);
|
|
300
|
+
case 400:
|
|
301
|
+
return new WeaveValidationError(message);
|
|
302
|
+
default:
|
|
303
|
+
return new WeaveError(message, status);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// src/push.ts
|
|
308
|
+
async function fetchIR(transport, base, key, name) {
|
|
309
|
+
const res = await transport(
|
|
310
|
+
new Request(`${base}/admin/entities/${encodeURIComponent(name)}`, { method: "GET", headers: { "x-api-key": key } })
|
|
311
|
+
);
|
|
312
|
+
if (!res.ok) return null;
|
|
313
|
+
return await res.json().catch(() => null);
|
|
314
|
+
}
|
|
315
|
+
function depsOf(ir) {
|
|
316
|
+
const deps = /* @__PURE__ */ new Set();
|
|
317
|
+
const walk = (fields) => {
|
|
318
|
+
for (const node of Object.values(fields)) {
|
|
319
|
+
if (node.kind === "reference") deps.add(node.target);
|
|
320
|
+
else if (node.kind === "owned") {
|
|
321
|
+
if (node.mirror) deps.add(node.mirror);
|
|
322
|
+
if (node.shape) walk(node.shape);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
walk(ir.fields);
|
|
327
|
+
return deps;
|
|
328
|
+
}
|
|
329
|
+
async function pushEntities(entities, options) {
|
|
330
|
+
const transport = options.fetch ?? ((req) => globalThis.fetch(req));
|
|
331
|
+
const base = options.url.replace(/\/$/, "");
|
|
332
|
+
const irs = Object.values(entities).map((e) => ({ name: e.name, ir: toIR(e) }));
|
|
333
|
+
const byName2 = new Map(irs.map((x) => [x.name, x]));
|
|
334
|
+
const ordered = [];
|
|
335
|
+
const seen = /* @__PURE__ */ new Set();
|
|
336
|
+
const visit = (x) => {
|
|
337
|
+
if (seen.has(x.name)) return;
|
|
338
|
+
seen.add(x.name);
|
|
339
|
+
for (const dep of depsOf(x.ir)) {
|
|
340
|
+
const d = byName2.get(dep);
|
|
341
|
+
if (d) visit(d);
|
|
342
|
+
}
|
|
343
|
+
ordered.push(x);
|
|
344
|
+
};
|
|
345
|
+
for (const x of irs) visit(x);
|
|
346
|
+
const applied = [];
|
|
347
|
+
const review = [];
|
|
348
|
+
for (const { name, ir } of ordered) {
|
|
349
|
+
const renameMap = options.renames?.[name];
|
|
350
|
+
if (renameMap && Object.keys(renameMap).length > 0) {
|
|
351
|
+
const existing = await fetchIR(transport, base, options.key, name);
|
|
352
|
+
if (existing) {
|
|
353
|
+
const idByOldName = /* @__PURE__ */ new Map();
|
|
354
|
+
for (const [fname, fnode] of Object.entries(existing.fields)) if (fnode.id) idByOldName.set(fname, fnode.id);
|
|
355
|
+
for (const [oldName, newName] of Object.entries(renameMap)) {
|
|
356
|
+
const id = idByOldName.get(oldName);
|
|
357
|
+
const target = ir.fields[newName];
|
|
358
|
+
if (id && target) ir.fields = { ...ir.fields, [newName]: { ...target, id } };
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
const body = { ir };
|
|
363
|
+
if (options.confirm?.[name]) body.confirm = options.confirm[name];
|
|
364
|
+
if (options.fill?.[name]) body.fill = options.fill[name];
|
|
365
|
+
const res = await transport(
|
|
366
|
+
new Request(`${base}/admin/entities/${encodeURIComponent(name)}`, {
|
|
367
|
+
method: "PUT",
|
|
368
|
+
headers: { "x-api-key": options.key, "content-type": "application/json" },
|
|
369
|
+
body: JSON.stringify(body)
|
|
370
|
+
})
|
|
371
|
+
);
|
|
372
|
+
const json3 = await res.json().catch(() => null);
|
|
373
|
+
if (res.status === 200) applied.push(name);
|
|
374
|
+
else if (res.status === 409 && json3?.plan) review.push({ name, plan: json3.plan });
|
|
375
|
+
else throw errorFor(res.status, json3?.error ?? `Push failed for '${name}' (${res.status}).`);
|
|
376
|
+
}
|
|
377
|
+
return { applied, review };
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// src/config.ts
|
|
381
|
+
function defineConfig(config = {}) {
|
|
382
|
+
return config;
|
|
383
|
+
}
|
|
384
|
+
var DEFAULT_DIR = "weave";
|
|
385
|
+
|
|
386
|
+
// src/scope.ts
|
|
387
|
+
function defineScope(name, entities) {
|
|
388
|
+
return { name, entities };
|
|
389
|
+
}
|
|
390
|
+
var isObj = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
391
|
+
function namePathToIds(entity, byName2, dotPath) {
|
|
392
|
+
const ids = [];
|
|
393
|
+
let fields = byName2.get(entity)?.fields ?? {};
|
|
394
|
+
const segs = dotPath.split(".");
|
|
395
|
+
for (let i = 0; i < segs.length; i++) {
|
|
396
|
+
const f = fields[segs[i]];
|
|
397
|
+
if (!f?.id) throw new Error(`scope: unknown field '${dotPath}' (em '${entity}').`);
|
|
398
|
+
ids.push(f.id);
|
|
399
|
+
if (i === segs.length - 1) break;
|
|
400
|
+
if (f.kind === "owned") fields = f.shape ?? {};
|
|
401
|
+
else if (f.kind === "reference") fields = byName2.get(f.target)?.fields ?? {};
|
|
402
|
+
else throw new Error(`scope: '${dotPath}' atravessa um escalar.`);
|
|
403
|
+
}
|
|
404
|
+
return ids;
|
|
405
|
+
}
|
|
406
|
+
function decodeOp(val) {
|
|
407
|
+
const ilikeUnwrap = (s) => {
|
|
408
|
+
if (typeof s === "string" && s.startsWith("%") && s.endsWith("%")) return { op: "contains", value: s.slice(1, -1) };
|
|
409
|
+
if (typeof s === "string" && s.endsWith("%")) return { op: "startsWith", value: s.slice(0, -1) };
|
|
410
|
+
return { op: "contains", value: s };
|
|
411
|
+
};
|
|
412
|
+
if ("ilike" in val) return ilikeUnwrap(val["ilike"]);
|
|
413
|
+
if ("eq" in val) return { op: "equals", value: val["eq"] };
|
|
414
|
+
if ("ne" in val) return { op: "notEquals", value: val["ne"] };
|
|
415
|
+
if ("gt" in val) return { op: "gt", value: val["gt"] };
|
|
416
|
+
if ("gte" in val) return { op: "gte", value: val["gte"] };
|
|
417
|
+
if ("lt" in val) return { op: "lt", value: val["lt"] };
|
|
418
|
+
if ("lte" in val) return { op: "lte", value: val["lte"] };
|
|
419
|
+
if ("in" in val) return { op: "in", value: val["in"] };
|
|
420
|
+
if ("notIn" in val) return { op: "notIn", value: val["notIn"] };
|
|
421
|
+
if ("isNull" in val) return { op: "isEmpty" };
|
|
422
|
+
return { op: "equals", value: void 0 };
|
|
423
|
+
}
|
|
424
|
+
function whereToFilter(where, entity, byName2) {
|
|
425
|
+
if (Array.isArray(where["and"])) {
|
|
426
|
+
return { and: where["and"].map((w) => whereToFilter(w, entity, byName2)) };
|
|
427
|
+
}
|
|
428
|
+
if (Array.isArray(where["or"])) {
|
|
429
|
+
return { or: where["or"].map((w) => whereToFilter(w, entity, byName2)) };
|
|
430
|
+
}
|
|
431
|
+
const idPath = [];
|
|
432
|
+
let cur = where;
|
|
433
|
+
let fields = byName2.get(entity)?.fields ?? {};
|
|
434
|
+
for (let guard = 0; guard < 16; guard++) {
|
|
435
|
+
const entry = Object.entries(cur)[0];
|
|
436
|
+
if (!entry) break;
|
|
437
|
+
const [key, val] = entry;
|
|
438
|
+
const f = fields[key];
|
|
439
|
+
if (!f?.id) throw new Error(`scope: campo '${key}' desconhecido em '${entity}'.`);
|
|
440
|
+
idPath.push(f.id);
|
|
441
|
+
if (f.kind === "column") {
|
|
442
|
+
const opObj = f.array && isObj(val) && "some" in val ? val["some"] : val;
|
|
443
|
+
const { op, value } = decodeOp(opObj);
|
|
444
|
+
return value === void 0 ? { path: idPath, op } : { path: idPath, op, value };
|
|
445
|
+
}
|
|
446
|
+
fields = f.kind === "owned" ? f.shape ?? {} : byName2.get(f.target)?.fields ?? {};
|
|
447
|
+
cur = isObj(val) && "some" in val ? val["some"] : val;
|
|
448
|
+
}
|
|
449
|
+
throw new Error(`scope: filtro inv\xE1lido em '${entity}'.`);
|
|
450
|
+
}
|
|
451
|
+
async function pushScopes(scopes, options) {
|
|
452
|
+
const transport = options.fetch ?? ((req) => globalThis.fetch(req));
|
|
453
|
+
const base = options.url.replace(/\/$/, "");
|
|
454
|
+
const listRes = await transport(
|
|
455
|
+
new Request(`${base}/admin/entities`, { method: "GET", headers: { "x-api-key": options.key } })
|
|
456
|
+
);
|
|
457
|
+
const listJson = await listRes.json().catch(() => null);
|
|
458
|
+
if (!listRes.ok || !listJson?.entities) {
|
|
459
|
+
throw errorFor(listRes.status, listJson?.error ?? "Failed to load entities for scope push.");
|
|
460
|
+
}
|
|
461
|
+
const byName2 = new Map(listJson.entities.map((e) => [e.name, e]));
|
|
462
|
+
const pushed = [];
|
|
463
|
+
for (const def of Object.values(scopes)) {
|
|
464
|
+
const entities = {};
|
|
465
|
+
for (const [entity, rule] of Object.entries(def.entities)) {
|
|
466
|
+
const rows = rule.where ? whereToFilter(rule.where, entity, byName2) : null;
|
|
467
|
+
let fields = null;
|
|
468
|
+
if (rule.fields?.include) {
|
|
469
|
+
fields = { mode: "include", paths: rule.fields.include.map((p) => namePathToIds(entity, byName2, p)) };
|
|
470
|
+
} else if (rule.fields?.exclude) {
|
|
471
|
+
fields = { mode: "exclude", paths: rule.fields.exclude.map((p) => namePathToIds(entity, byName2, p)) };
|
|
472
|
+
}
|
|
473
|
+
entities[entity] = { verbs: rule.verbs, rows, fields };
|
|
474
|
+
}
|
|
475
|
+
const res = await transport(
|
|
476
|
+
new Request(`${base}/admin/scopes/${encodeURIComponent(def.name)}`, {
|
|
477
|
+
method: "PUT",
|
|
478
|
+
headers: { "x-api-key": options.key, "content-type": "application/json" },
|
|
479
|
+
body: JSON.stringify({ name: def.name, entities })
|
|
480
|
+
})
|
|
481
|
+
);
|
|
482
|
+
if (!res.ok) {
|
|
483
|
+
const j = await res.json().catch(() => null);
|
|
484
|
+
throw errorFor(res.status, j?.error ?? `Push scope '${def.name}' failed (${res.status}).`);
|
|
485
|
+
}
|
|
486
|
+
pushed.push(def.name);
|
|
487
|
+
}
|
|
488
|
+
return { pushed };
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// src/gen.ts
|
|
492
|
+
function baseExpr(node, ctx, self) {
|
|
493
|
+
if (node.kind === "column") {
|
|
494
|
+
ctx.builders.add(node.type);
|
|
495
|
+
if (node.array) {
|
|
496
|
+
ctx.builders.add("array");
|
|
497
|
+
let s2 = `array(${node.type}())`;
|
|
498
|
+
if (node.notNull === false) s2 += ".nullable()";
|
|
499
|
+
if (node.unique) s2 += ".unique()";
|
|
500
|
+
if (node.index) s2 += ".index()";
|
|
501
|
+
return s2;
|
|
502
|
+
}
|
|
503
|
+
let s = `${node.type}()`;
|
|
504
|
+
if (node.notNull) s += ".notNull()";
|
|
505
|
+
if (node.unique) s += ".unique()";
|
|
506
|
+
if (node.index) s += ".index()";
|
|
507
|
+
if (node.default !== void 0) s += `.default(${JSON.stringify(node.default)})`;
|
|
508
|
+
return s;
|
|
509
|
+
}
|
|
510
|
+
if (node.kind === "reference") {
|
|
511
|
+
ctx.builders.add("reference");
|
|
512
|
+
if (node.target !== self) ctx.imports.add(node.target);
|
|
513
|
+
if (node.cardinality === "many") {
|
|
514
|
+
ctx.builders.add("array");
|
|
515
|
+
return `reference(array(${node.target}))`;
|
|
516
|
+
}
|
|
517
|
+
return `reference(${node.target})${node.notNull ? ".notNull()" : ""}`;
|
|
518
|
+
}
|
|
519
|
+
if (node.mirror) ctx.mirror = true;
|
|
520
|
+
ctx.builders.add("owned");
|
|
521
|
+
const inner = shapeSource(node.shape ?? {}, ctx, self);
|
|
522
|
+
return node.array ? `owned(array({ ${inner} }))` : `owned({ ${inner} })`;
|
|
523
|
+
}
|
|
524
|
+
function fieldSource(node, ctx, self) {
|
|
525
|
+
const base = baseExpr(node, ctx, self);
|
|
526
|
+
const id = ctx.withId && node.id ? `.$id(${JSON.stringify(node.id)})` : "";
|
|
527
|
+
return base + id;
|
|
528
|
+
}
|
|
529
|
+
function shapeSource(fields, ctx, self) {
|
|
530
|
+
return Object.entries(fields).map(([k, n]) => `${k}: ${fieldSource(n, ctx, self)}`).join(", ");
|
|
531
|
+
}
|
|
532
|
+
function irToSource(ir, options = {}) {
|
|
533
|
+
const ctx = { builders: /* @__PURE__ */ new Set(), imports: /* @__PURE__ */ new Set(), mirror: false, withId: options.withId ?? false };
|
|
534
|
+
const body = Object.entries(ir.fields).map(([k, n]) => ` ${k}: ${fieldSource(n, ctx, ir.name)},`).join("\n");
|
|
535
|
+
const builders = ["defineEntity", ...[...ctx.builders].sort()];
|
|
536
|
+
const lines = [`import { ${builders.join(", ")} } from "@mauroandre/weave-sdk";`];
|
|
537
|
+
for (const t of [...ctx.imports].sort()) lines.push(`import ${t} from "./${t}.js";`);
|
|
538
|
+
if (ctx.mirror) lines.push(`// \u26A0 esta entidade usa mirror \u2014 gere/edite a forma \xE0 m\xE3o (o builder n\xE3o tem mirror()).`);
|
|
539
|
+
lines.push("", `export default defineEntity(${JSON.stringify(ir.name)}, {`, body, "});", "");
|
|
540
|
+
return lines.join("\n");
|
|
541
|
+
}
|
|
542
|
+
function idPathToNames(entity, byName2, idPath) {
|
|
543
|
+
const names = [];
|
|
544
|
+
let fields = byName2.get(entity)?.fields ?? {};
|
|
545
|
+
for (let i = 0; i < idPath.length; i++) {
|
|
546
|
+
const entry = Object.entries(fields).find(([, f2]) => f2.id === idPath[i]);
|
|
547
|
+
if (!entry) throw new Error(`scope gen: field id '${idPath[i]}' n\xE3o encontrado em '${entity}'.`);
|
|
548
|
+
const [name, f] = entry;
|
|
549
|
+
names.push(name);
|
|
550
|
+
if (i === idPath.length - 1) break;
|
|
551
|
+
if (f.kind === "owned") fields = f.shape ?? {};
|
|
552
|
+
else if (f.kind === "reference") fields = byName2.get(f.target)?.fields ?? {};
|
|
553
|
+
else throw new Error(`scope gen: caminho atravessa um escalar em '${entity}'.`);
|
|
554
|
+
}
|
|
555
|
+
return names;
|
|
556
|
+
}
|
|
557
|
+
function opToWhere(op, value) {
|
|
558
|
+
const str = typeof value === "string";
|
|
559
|
+
switch (op) {
|
|
560
|
+
case "contains":
|
|
561
|
+
return { ilike: str ? `%${value}%` : value };
|
|
562
|
+
case "startsWith":
|
|
563
|
+
return { ilike: str ? `${value}%` : value };
|
|
564
|
+
case "equals":
|
|
565
|
+
case "on":
|
|
566
|
+
return { eq: value };
|
|
567
|
+
case "notEquals":
|
|
568
|
+
return { ne: value };
|
|
569
|
+
case "gt":
|
|
570
|
+
case "gte":
|
|
571
|
+
case "lt":
|
|
572
|
+
case "lte":
|
|
573
|
+
return { [op]: value };
|
|
574
|
+
case "before":
|
|
575
|
+
return { lt: value };
|
|
576
|
+
case "after":
|
|
577
|
+
return { gt: value };
|
|
578
|
+
case "in":
|
|
579
|
+
return { in: value };
|
|
580
|
+
case "notIn":
|
|
581
|
+
return { notIn: value };
|
|
582
|
+
case "isTrue":
|
|
583
|
+
return { eq: true };
|
|
584
|
+
case "isFalse":
|
|
585
|
+
return { eq: false };
|
|
586
|
+
case "isEmpty":
|
|
587
|
+
return { isNull: true };
|
|
588
|
+
default:
|
|
589
|
+
return { eq: value };
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
function buildWhere(entity, byName2, names, op, value) {
|
|
593
|
+
const rec = (fields, idx) => {
|
|
594
|
+
const seg = names[idx];
|
|
595
|
+
const f = fields[seg];
|
|
596
|
+
if (idx === names.length - 1) {
|
|
597
|
+
const sc = opToWhere(op, value);
|
|
598
|
+
const isArray = f?.kind === "column" && f.array === true;
|
|
599
|
+
return { [seg]: isArray && op !== "isEmpty" ? { some: sc } : sc };
|
|
600
|
+
}
|
|
601
|
+
let next = {};
|
|
602
|
+
let toMany = false;
|
|
603
|
+
if (f?.kind === "owned") {
|
|
604
|
+
next = f.shape ?? {};
|
|
605
|
+
toMany = f.array === true;
|
|
606
|
+
} else if (f?.kind === "reference") {
|
|
607
|
+
next = byName2.get(f.target)?.fields ?? {};
|
|
608
|
+
toMany = f.cardinality === "many";
|
|
609
|
+
}
|
|
610
|
+
const inner = rec(next, idx + 1);
|
|
611
|
+
return { [seg]: toMany ? { some: inner } : inner };
|
|
612
|
+
};
|
|
613
|
+
return rec(byName2.get(entity)?.fields ?? {}, 0);
|
|
614
|
+
}
|
|
615
|
+
function filterToWhere(filter, entity, byName2) {
|
|
616
|
+
if ("and" in filter) return { and: filter.and.map((f) => filterToWhere(f, entity, byName2)) };
|
|
617
|
+
if ("or" in filter) return { or: filter.or.map((f) => filterToWhere(f, entity, byName2)) };
|
|
618
|
+
const names = idPathToNames(entity, byName2, filter.path);
|
|
619
|
+
return buildWhere(entity, byName2, names, filter.op, filter.value);
|
|
620
|
+
}
|
|
621
|
+
var IDENT = /^[A-Za-z_$][\w$]*$/;
|
|
622
|
+
function lit(value, indent = 0) {
|
|
623
|
+
if (value === null) return "null";
|
|
624
|
+
if (typeof value === "string") return JSON.stringify(value);
|
|
625
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
626
|
+
if (Array.isArray(value)) return `[${value.map((v) => lit(v, indent)).join(", ")}]`;
|
|
627
|
+
if (typeof value === "object") {
|
|
628
|
+
const entries = Object.entries(value);
|
|
629
|
+
if (entries.length === 0) return "{}";
|
|
630
|
+
const pad = " ".repeat(indent + 1);
|
|
631
|
+
const close = " ".repeat(indent);
|
|
632
|
+
const body = entries.map(([k, v]) => `${pad}${IDENT.test(k) ? k : JSON.stringify(k)}: ${lit(v, indent + 1)}`).join(",\n");
|
|
633
|
+
return `{
|
|
634
|
+
${body},
|
|
635
|
+
${close}}`;
|
|
636
|
+
}
|
|
637
|
+
return JSON.stringify(value);
|
|
638
|
+
}
|
|
639
|
+
function scopeToSource(scope, byName2) {
|
|
640
|
+
const rules = {};
|
|
641
|
+
for (const [entity, rule] of Object.entries(scope.entities)) {
|
|
642
|
+
const out = { verbs: rule.verbs };
|
|
643
|
+
if (rule.rows) out["where"] = filterToWhere(rule.rows, entity, byName2);
|
|
644
|
+
if (rule.fields) {
|
|
645
|
+
const paths = rule.fields.paths.map((p) => idPathToNames(entity, byName2, p).join("."));
|
|
646
|
+
out["fields"] = rule.fields.mode === "include" ? { include: paths } : { exclude: paths };
|
|
647
|
+
}
|
|
648
|
+
rules[entity] = out;
|
|
649
|
+
}
|
|
650
|
+
return [
|
|
651
|
+
`import { defineScope } from "@mauroandre/weave-sdk";`,
|
|
652
|
+
"",
|
|
653
|
+
`export default defineScope(${JSON.stringify(scope.name)}, ${lit(rules)});`,
|
|
654
|
+
""
|
|
655
|
+
].join("\n");
|
|
656
|
+
}
|
|
657
|
+
function barrelSource(names) {
|
|
658
|
+
return [
|
|
659
|
+
`// GERADO por \`weave gen\` \u2014 n\xE3o edite \xE0 m\xE3o.`,
|
|
660
|
+
...names.map((n) => `export { default as ${n} } from "./${n}.js";`),
|
|
661
|
+
""
|
|
662
|
+
].join("\n");
|
|
663
|
+
}
|
|
664
|
+
function clientSource() {
|
|
665
|
+
return [
|
|
666
|
+
`// GERADO por \`weave gen\` \u2014 n\xE3o edite \xE0 m\xE3o. Uso server-side (a key \xE9 segredo).`,
|
|
667
|
+
`import { createClient } from "@mauroandre/weave-sdk";`,
|
|
668
|
+
`import * as entities from "./entities/index.js";`,
|
|
669
|
+
"",
|
|
670
|
+
`export const weave = createClient({`,
|
|
671
|
+
` url: process.env.WEAVE_URL!,`,
|
|
672
|
+
` key: process.env.WEAVE_KEY!,`,
|
|
673
|
+
` entities,`,
|
|
674
|
+
`});`,
|
|
675
|
+
""
|
|
676
|
+
].join("\n");
|
|
677
|
+
}
|
|
678
|
+
async function fetchJson(transport, url, key, what) {
|
|
679
|
+
const res = await transport(new Request(url, { method: "GET", headers: { "x-api-key": key } }));
|
|
680
|
+
const json3 = await res.json().catch(() => null);
|
|
681
|
+
if (!res.ok || !json3) throw errorFor(res.status, json3?.error ?? `Failed to load ${what}.`);
|
|
682
|
+
return json3;
|
|
683
|
+
}
|
|
684
|
+
async function genProject(options) {
|
|
685
|
+
const transport = options.fetch ?? ((req) => globalThis.fetch(req));
|
|
686
|
+
const base = options.url.replace(/\/$/, "");
|
|
687
|
+
const ents = await fetchJson(transport, `${base}/admin/entities`, options.key, "entities");
|
|
688
|
+
const scps = await fetchJson(transport, `${base}/admin/scopes`, options.key, "scopes");
|
|
689
|
+
const byName2 = new Map(ents.entities.map((e) => [e.name, e]));
|
|
690
|
+
const files = {};
|
|
691
|
+
const entityNames = [];
|
|
692
|
+
for (const ir of ents.entities) {
|
|
693
|
+
files[`entities/${ir.name}.ts`] = irToSource(ir, { withId: true });
|
|
694
|
+
entityNames.push(ir.name);
|
|
695
|
+
}
|
|
696
|
+
entityNames.sort();
|
|
697
|
+
files["entities/index.ts"] = barrelSource(entityNames);
|
|
698
|
+
const scopeNames = [];
|
|
699
|
+
for (const s of scps.scopes) {
|
|
700
|
+
try {
|
|
701
|
+
files[`scopes/${s.name}.ts`] = scopeToSource(s, byName2);
|
|
702
|
+
scopeNames.push(s.name);
|
|
703
|
+
} catch {
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
scopeNames.sort();
|
|
707
|
+
files["scopes/index.ts"] = barrelSource(scopeNames);
|
|
708
|
+
files["index.ts"] = clientSource();
|
|
709
|
+
return { files, entities: entityNames, scopes: scopeNames };
|
|
710
|
+
}
|
|
711
|
+
async function pullEntities(options) {
|
|
712
|
+
const transport = options.fetch ?? ((req) => globalThis.fetch(req));
|
|
713
|
+
const base = options.url.replace(/\/$/, "");
|
|
714
|
+
const json3 = await fetchJson(transport, `${base}/admin/entities`, options.key, "entities");
|
|
715
|
+
const files = {};
|
|
716
|
+
const names = [];
|
|
717
|
+
for (const ir of json3.entities) {
|
|
718
|
+
files[`${ir.name}.ts`] = irToSource(ir);
|
|
719
|
+
names.push(ir.name);
|
|
720
|
+
}
|
|
721
|
+
return { files, names: names.sort() };
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
export {
|
|
725
|
+
Column,
|
|
726
|
+
Owned,
|
|
727
|
+
owned,
|
|
728
|
+
Reference,
|
|
729
|
+
reference,
|
|
730
|
+
int22 as int2,
|
|
731
|
+
int42 as int4,
|
|
732
|
+
int82 as int8,
|
|
733
|
+
numeric2 as numeric,
|
|
734
|
+
float42 as float4,
|
|
735
|
+
float82 as float8,
|
|
736
|
+
text2 as text,
|
|
737
|
+
varchar2 as varchar,
|
|
738
|
+
bpchar2 as bpchar,
|
|
739
|
+
timestamptz2 as timestamptz,
|
|
740
|
+
timestamp2 as timestamp,
|
|
741
|
+
date2 as date,
|
|
742
|
+
time2 as time,
|
|
743
|
+
interval2 as interval,
|
|
744
|
+
bool2 as bool,
|
|
745
|
+
uuid2 as uuid,
|
|
746
|
+
json2 as json,
|
|
747
|
+
jsonb2 as jsonb,
|
|
748
|
+
bytea2 as bytea,
|
|
749
|
+
array,
|
|
750
|
+
defineEntity,
|
|
751
|
+
WeaveError,
|
|
752
|
+
WeaveAuthError,
|
|
753
|
+
WeaveScopeError,
|
|
754
|
+
WeaveNotFoundError,
|
|
755
|
+
WeaveValidationError,
|
|
756
|
+
errorFor,
|
|
757
|
+
pushEntities,
|
|
758
|
+
defineConfig,
|
|
759
|
+
DEFAULT_DIR,
|
|
760
|
+
defineScope,
|
|
761
|
+
pushScopes,
|
|
762
|
+
irToSource,
|
|
763
|
+
scopeToSource,
|
|
764
|
+
genProject,
|
|
765
|
+
pullEntities
|
|
766
|
+
};
|
|
767
|
+
//# sourceMappingURL=chunk-PWTED7ZO.js.map
|