@happyvertical/smrt-properties 0.37.2 → 0.37.3
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/index.js +711 -767
- package/dist/index.js.map +1 -1
- package/dist/manifest.json +2 -2
- package/dist/smrt-knowledge.json +7 -7
- package/dist/types.js +0 -2
- package/package.json +10 -10
- package/dist/types.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,788 +1,732 @@
|
|
|
1
|
-
import { ObjectRegistry,
|
|
1
|
+
import { ObjectRegistry, SmrtCollection, SmrtHierarchical, SmrtObject, crossPackageRef, foreignKey, smrt } from "@happyvertical/smrt-core";
|
|
2
2
|
import { definePrompt, resolvePrompt } from "@happyvertical/smrt-prompts";
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
)
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
import { TenantScoped, queryGlobal, queryWithGlobals, tenantId } from "@happyvertical/smrt-tenancy";
|
|
4
|
+
//#region \0rolldown/runtime.js
|
|
5
|
+
var __defProp$2 = Object.defineProperty;
|
|
6
|
+
var __exportAll = (all, no_symbols) => {
|
|
7
|
+
let target = {};
|
|
8
|
+
for (var name in all) __defProp$2(target, name, {
|
|
9
|
+
get: all[name],
|
|
10
|
+
enumerable: true
|
|
11
|
+
});
|
|
12
|
+
if (!no_symbols) __defProp$2(target, Symbol.toStringTag, { value: "Module" });
|
|
13
|
+
return target;
|
|
14
|
+
};
|
|
15
|
+
//#endregion
|
|
16
|
+
//#region src/__smrt-register__.ts
|
|
17
|
+
ObjectRegistry.registerPackageManifest(new URL("./manifest.json", "" + import.meta.url));
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region src/prompts.ts
|
|
20
|
+
var smrtPropertiesSummarizePrompt = definePrompt({
|
|
21
|
+
key: "smrtProperties.property.summarize",
|
|
22
|
+
template: `Summarize this digital property:
|
|
10
23
|
Name: {propertyName}
|
|
11
24
|
Domain: {propertyDomain}
|
|
12
25
|
Description: {propertyDescription}
|
|
13
26
|
Status: {propertyStatus}
|
|
14
27
|
Total zones: {totalZones}
|
|
15
28
|
Top-level zones: {topLevelZones}`,
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
29
|
+
editable: {
|
|
30
|
+
template: true,
|
|
31
|
+
profile: true,
|
|
32
|
+
model: true,
|
|
33
|
+
params: true
|
|
34
|
+
}
|
|
22
35
|
});
|
|
23
36
|
function promptMessageOptions(ai) {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
37
|
+
return {
|
|
38
|
+
...ai.params || {},
|
|
39
|
+
...ai.model ? { model: ai.model } : {},
|
|
40
|
+
...typeof ai.temperature === "number" ? { temperature: ai.temperature } : {},
|
|
41
|
+
...typeof ai.maxTokens === "number" ? { maxTokens: ai.maxTokens } : {}
|
|
42
|
+
};
|
|
30
43
|
}
|
|
44
|
+
//#endregion
|
|
45
|
+
//#region src/models/Property.ts
|
|
31
46
|
var __defProp$1 = Object.defineProperty;
|
|
32
47
|
var __getOwnPropDesc$1 = Object.getOwnPropertyDescriptor;
|
|
33
48
|
var __decorateClass$1 = (decorators, target, key, kind) => {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
if (kind && result) __defProp$1(target, key, result);
|
|
39
|
-
return result;
|
|
49
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$1(target, key) : target;
|
|
50
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
51
|
+
if (kind && result) __defProp$1(target, key, result);
|
|
52
|
+
return result;
|
|
40
53
|
};
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
totalZones: String(zones.length),
|
|
151
|
-
topLevelZones: topLevelZones.map((z) => z.name).join(", ")
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
);
|
|
155
|
-
const ai = await this.getAiClient();
|
|
156
|
-
const response = await ai.message(
|
|
157
|
-
resolvedPrompt.text,
|
|
158
|
-
promptMessageOptions(resolvedPrompt.ai)
|
|
159
|
-
);
|
|
160
|
-
return response.trim();
|
|
161
|
-
}
|
|
54
|
+
var Property = class extends SmrtObject {
|
|
55
|
+
tenantId = null;
|
|
56
|
+
/**
|
|
57
|
+
* Display name of the property
|
|
58
|
+
*/
|
|
59
|
+
name = "";
|
|
60
|
+
/**
|
|
61
|
+
* Domain name (e.g., "oakcreeknews.com")
|
|
62
|
+
*/
|
|
63
|
+
domain = "";
|
|
64
|
+
/**
|
|
65
|
+
* Full URL (e.g., "https://oakcreeknews.com")
|
|
66
|
+
*/
|
|
67
|
+
url = "";
|
|
68
|
+
/**
|
|
69
|
+
* Property description
|
|
70
|
+
*/
|
|
71
|
+
description = "";
|
|
72
|
+
repositoryId = null;
|
|
73
|
+
ownerId = null;
|
|
74
|
+
/**
|
|
75
|
+
* Property status
|
|
76
|
+
*/
|
|
77
|
+
status = "active";
|
|
78
|
+
/**
|
|
79
|
+
* Extensible metadata (analytics IDs, config, etc.)
|
|
80
|
+
*/
|
|
81
|
+
metadata = {};
|
|
82
|
+
constructor(options = {}) {
|
|
83
|
+
super(options);
|
|
84
|
+
if (options.tenantId !== void 0) this.tenantId = options.tenantId;
|
|
85
|
+
if (options.name !== void 0) this.name = options.name;
|
|
86
|
+
if (options.domain !== void 0) this.domain = options.domain;
|
|
87
|
+
if (options.url !== void 0) this.url = options.url;
|
|
88
|
+
if (options.description !== void 0) this.description = options.description;
|
|
89
|
+
if (options.repositoryId !== void 0) this.repositoryId = options.repositoryId;
|
|
90
|
+
if (options.ownerId !== void 0) this.ownerId = options.ownerId;
|
|
91
|
+
if (options.status !== void 0) this.status = options.status;
|
|
92
|
+
if (options.metadata !== void 0) this.metadata = options.metadata;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Get all zones for this property
|
|
96
|
+
*/
|
|
97
|
+
async getZones() {
|
|
98
|
+
if (!this.id) return [];
|
|
99
|
+
const { ZoneCollection } = await Promise.resolve().then(() => Zones_exports);
|
|
100
|
+
return await (await ZoneCollection.create(this.options)).findByProperty(this.id);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Get zone tree for this property
|
|
104
|
+
*/
|
|
105
|
+
async getZoneTree() {
|
|
106
|
+
if (!this.id) return {
|
|
107
|
+
propertyId: "",
|
|
108
|
+
roots: []
|
|
109
|
+
};
|
|
110
|
+
const { ZoneCollection } = await Promise.resolve().then(() => Zones_exports);
|
|
111
|
+
return await (await ZoneCollection.create(this.options)).getTree(this.id);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Create a zone on this property
|
|
115
|
+
*/
|
|
116
|
+
async createZone(options) {
|
|
117
|
+
if (!this.id) throw new Error("Property must be saved before creating zones");
|
|
118
|
+
const { ZoneCollection } = await Promise.resolve().then(() => Zones_exports);
|
|
119
|
+
const zone = await (await ZoneCollection.create(this.options)).create({
|
|
120
|
+
...options,
|
|
121
|
+
propertyId: this.id
|
|
122
|
+
});
|
|
123
|
+
await zone.save();
|
|
124
|
+
return zone;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Check if property is active
|
|
128
|
+
*/
|
|
129
|
+
isActive() {
|
|
130
|
+
return this.status === "active";
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* AI-powered: Generate a summary of the property and its zones.
|
|
134
|
+
*
|
|
135
|
+
* Uses the `smrtProperties.property.summarize` prompt registered via
|
|
136
|
+
* `@happyvertical/smrt-prompts`, allowing tenant- or instance-level
|
|
137
|
+
* overrides of the template, model, and parameters at runtime.
|
|
138
|
+
*
|
|
139
|
+
* Only non-PII fields (name, domain, description, status) plus aggregate
|
|
140
|
+
* zone information are sent to the AI provider. Internal foreign-key
|
|
141
|
+
* fields and the extensible `metadata` blob are intentionally excluded.
|
|
142
|
+
*
|
|
143
|
+
* @returns Generated summary text
|
|
144
|
+
*/
|
|
145
|
+
async summarize() {
|
|
146
|
+
const zones = await this.getZones();
|
|
147
|
+
const topLevelZones = zones.filter((z) => z.parentId === null);
|
|
148
|
+
const db = this.options.db ?? this.options.persistence;
|
|
149
|
+
const resolvedPrompt = await resolvePrompt(smrtPropertiesSummarizePrompt.key, {
|
|
150
|
+
db,
|
|
151
|
+
tenantId: this.tenantId,
|
|
152
|
+
variables: {
|
|
153
|
+
propertyName: this.name || "",
|
|
154
|
+
propertyDomain: this.domain || "",
|
|
155
|
+
propertyDescription: this.description || "",
|
|
156
|
+
propertyStatus: this.status || "",
|
|
157
|
+
totalZones: String(zones.length),
|
|
158
|
+
topLevelZones: topLevelZones.map((z) => z.name).join(", ")
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
return (await (await this.getAiClient()).message(resolvedPrompt.text, promptMessageOptions(resolvedPrompt.ai))).trim();
|
|
162
|
+
}
|
|
162
163
|
};
|
|
163
|
-
__decorateClass$1([
|
|
164
|
-
|
|
165
|
-
], Property.prototype, "
|
|
166
|
-
__decorateClass$1([
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
*
|
|
308
|
-
* @param tenantId - Tenant ID to filter by
|
|
309
|
-
* @returns Array of tenant-specific and global properties
|
|
310
|
-
*/
|
|
311
|
-
async findWithGlobals(tenantId2) {
|
|
312
|
-
return queryWithGlobals(
|
|
313
|
-
this,
|
|
314
|
-
tenantId2,
|
|
315
|
-
"Property.findWithGlobals"
|
|
316
|
-
);
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
const Properties = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
320
|
-
__proto__: null,
|
|
321
|
-
PropertyCollection
|
|
322
|
-
}, Symbol.toStringTag, { value: "Module" }));
|
|
164
|
+
__decorateClass$1([tenantId({ nullable: true })], Property.prototype, "tenantId", 2);
|
|
165
|
+
__decorateClass$1([crossPackageRef("@happyvertical/smrt-projects:Repository")], Property.prototype, "repositoryId", 2);
|
|
166
|
+
__decorateClass$1([crossPackageRef("@happyvertical/smrt-profiles:Profile")], Property.prototype, "ownerId", 2);
|
|
167
|
+
Property = __decorateClass$1([TenantScoped({ mode: "optional" }), smrt({
|
|
168
|
+
tableStrategy: "sti",
|
|
169
|
+
api: { include: [
|
|
170
|
+
"list",
|
|
171
|
+
"get",
|
|
172
|
+
"create",
|
|
173
|
+
"update",
|
|
174
|
+
"delete"
|
|
175
|
+
] },
|
|
176
|
+
mcp: { include: [
|
|
177
|
+
"list",
|
|
178
|
+
"get",
|
|
179
|
+
"create"
|
|
180
|
+
] },
|
|
181
|
+
cli: true
|
|
182
|
+
})], Property);
|
|
183
|
+
//#endregion
|
|
184
|
+
//#region src/collections/Properties.ts
|
|
185
|
+
var Properties_exports = /* @__PURE__ */ __exportAll({ PropertyCollection: () => PropertyCollection });
|
|
186
|
+
var PropertyCollection = class extends SmrtCollection {
|
|
187
|
+
static _itemClass = Property;
|
|
188
|
+
/**
|
|
189
|
+
* Find a property by domain
|
|
190
|
+
*
|
|
191
|
+
* @param domain - Domain name
|
|
192
|
+
* @returns Property or null
|
|
193
|
+
*/
|
|
194
|
+
async findByDomain(domain) {
|
|
195
|
+
return (await this.list({
|
|
196
|
+
where: { domain },
|
|
197
|
+
limit: 1
|
|
198
|
+
}))[0] || null;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Find properties by repository ID
|
|
202
|
+
*
|
|
203
|
+
* @param repositoryId - Repository ID
|
|
204
|
+
* @returns Array of properties
|
|
205
|
+
*/
|
|
206
|
+
async findByRepository(repositoryId) {
|
|
207
|
+
return await this.list({ where: { repositoryId } });
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Find properties by owner ID
|
|
211
|
+
*
|
|
212
|
+
* @param ownerId - Owner profile ID
|
|
213
|
+
* @returns Array of properties
|
|
214
|
+
*/
|
|
215
|
+
async findByOwner(ownerId) {
|
|
216
|
+
return await this.list({ where: { ownerId } });
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Find active properties
|
|
220
|
+
*
|
|
221
|
+
* @returns Array of active properties
|
|
222
|
+
*/
|
|
223
|
+
async findActive() {
|
|
224
|
+
return await this.list({ where: { status: "active" } });
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Find properties by status
|
|
228
|
+
*
|
|
229
|
+
* @param status - Property status
|
|
230
|
+
* @returns Array of properties with the given status
|
|
231
|
+
*/
|
|
232
|
+
async findByStatus(status) {
|
|
233
|
+
return await this.list({ where: { status } });
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Get or create a property by domain
|
|
237
|
+
*
|
|
238
|
+
* @param domain - Domain name
|
|
239
|
+
* @param defaults - Default values for creation
|
|
240
|
+
* @returns Property (existing or newly created)
|
|
241
|
+
*/
|
|
242
|
+
async getOrCreateByDomain(domain, defaults = {}) {
|
|
243
|
+
let property = await this.findByDomain(domain);
|
|
244
|
+
if (!property) {
|
|
245
|
+
property = await this.create({
|
|
246
|
+
domain,
|
|
247
|
+
name: defaults.name || domain,
|
|
248
|
+
url: defaults.url || `https://${domain}`,
|
|
249
|
+
repositoryId: defaults.repositoryId ?? null,
|
|
250
|
+
ownerId: defaults.ownerId ?? null,
|
|
251
|
+
status: "active"
|
|
252
|
+
});
|
|
253
|
+
await property.save();
|
|
254
|
+
}
|
|
255
|
+
return property;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Count properties by status
|
|
259
|
+
*
|
|
260
|
+
* @returns Object with counts per status
|
|
261
|
+
*/
|
|
262
|
+
async countByStatus() {
|
|
263
|
+
const all = await this.list({});
|
|
264
|
+
const counts = {
|
|
265
|
+
active: 0,
|
|
266
|
+
inactive: 0,
|
|
267
|
+
pending: 0
|
|
268
|
+
};
|
|
269
|
+
for (const prop of all) counts[prop.status]++;
|
|
270
|
+
return counts;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Find properties belonging to a specific tenant
|
|
274
|
+
*
|
|
275
|
+
* @param tenantId - Tenant ID to filter by
|
|
276
|
+
* @returns Array of properties for the tenant
|
|
277
|
+
*/
|
|
278
|
+
async findByTenant(tenantId) {
|
|
279
|
+
return this.list({ where: { tenantId } });
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Find all global properties (no tenant association).
|
|
283
|
+
*
|
|
284
|
+
* Routes through the shared tenant-global helper so it does not throw under
|
|
285
|
+
* an active tenant context (an explicit `tenant_id IS NULL` filter would be
|
|
286
|
+
* flagged as an isolation violation). (#1600)
|
|
287
|
+
*
|
|
288
|
+
* @returns Array of global properties
|
|
289
|
+
*/
|
|
290
|
+
async findGlobal() {
|
|
291
|
+
return queryGlobal(this);
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Find properties for a tenant plus all global properties.
|
|
295
|
+
*
|
|
296
|
+
* Fails closed if an active tenant context requests a different tenant's
|
|
297
|
+
* rows; the admin/system path keeps the cross-tenant capability. (#1600)
|
|
298
|
+
*
|
|
299
|
+
* @param tenantId - Tenant ID to filter by
|
|
300
|
+
* @returns Array of tenant-specific and global properties
|
|
301
|
+
*/
|
|
302
|
+
async findWithGlobals(tenantId) {
|
|
303
|
+
return queryWithGlobals(this, tenantId, "Property.findWithGlobals");
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
//#endregion
|
|
307
|
+
//#region src/models/Zone.ts
|
|
323
308
|
var __defProp = Object.defineProperty;
|
|
324
309
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
325
310
|
var __decorateClass = (decorators, target, key, kind) => {
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
if (kind && result) __defProp(target, key, result);
|
|
331
|
-
return result;
|
|
311
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
312
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
313
|
+
if (kind && result) __defProp(target, key, result);
|
|
314
|
+
return result;
|
|
332
315
|
};
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
async toTreeNode() {
|
|
464
|
-
const children = await this.getChildren();
|
|
465
|
-
const childNodes = await Promise.all(children.map((c) => c.toTreeNode()));
|
|
466
|
-
return {
|
|
467
|
-
zone: this,
|
|
468
|
-
children: childNodes
|
|
469
|
-
};
|
|
470
|
-
}
|
|
471
|
-
/**
|
|
472
|
-
* Check if a format is allowed in this zone
|
|
473
|
-
*/
|
|
474
|
-
isFormatAllowed(format) {
|
|
475
|
-
if (this.allowedFormats.length === 0) return true;
|
|
476
|
-
return this.allowedFormats.includes(format);
|
|
477
|
-
}
|
|
316
|
+
var Zone = class extends SmrtHierarchical {
|
|
317
|
+
tenantId = null;
|
|
318
|
+
propertyId = "";
|
|
319
|
+
/**
|
|
320
|
+
* Display name
|
|
321
|
+
*/
|
|
322
|
+
name = "";
|
|
323
|
+
/**
|
|
324
|
+
* Zone type (descriptive, not structural)
|
|
325
|
+
* Common values: "page", "section", "slot", "container", "widget"
|
|
326
|
+
*/
|
|
327
|
+
type = "";
|
|
328
|
+
/**
|
|
329
|
+
* URL path pattern (e.g., "/", "/articles/*")
|
|
330
|
+
*/
|
|
331
|
+
path = "";
|
|
332
|
+
/**
|
|
333
|
+
* CSS selector for placement (e.g., "#header", ".sidebar")
|
|
334
|
+
*/
|
|
335
|
+
selector = "";
|
|
336
|
+
/**
|
|
337
|
+
* Position hint (e.g., "top", "after-paragraph-3")
|
|
338
|
+
*/
|
|
339
|
+
position = "";
|
|
340
|
+
/**
|
|
341
|
+
* Width in pixels (null = fluid)
|
|
342
|
+
*/
|
|
343
|
+
width = null;
|
|
344
|
+
/**
|
|
345
|
+
* Height in pixels (null = fluid)
|
|
346
|
+
*/
|
|
347
|
+
height = null;
|
|
348
|
+
/**
|
|
349
|
+
* Allowed content/ad formats
|
|
350
|
+
*/
|
|
351
|
+
allowedFormats = [];
|
|
352
|
+
/**
|
|
353
|
+
* Default content/asset ID for fallback
|
|
354
|
+
*/
|
|
355
|
+
defaultContentId = null;
|
|
356
|
+
/**
|
|
357
|
+
* Extensible metadata
|
|
358
|
+
*/
|
|
359
|
+
metadata = {};
|
|
360
|
+
constructor(options = {}) {
|
|
361
|
+
super(options);
|
|
362
|
+
if (options.tenantId !== void 0) this.tenantId = options.tenantId;
|
|
363
|
+
if (options.propertyId !== void 0) this.propertyId = options.propertyId;
|
|
364
|
+
if (options.parentId !== void 0) this.parentId = options.parentId;
|
|
365
|
+
if (options.name !== void 0) this.name = options.name;
|
|
366
|
+
if (options.type !== void 0) this.type = options.type;
|
|
367
|
+
if (options.path !== void 0) this.path = options.path;
|
|
368
|
+
if (options.selector !== void 0) this.selector = options.selector;
|
|
369
|
+
if (options.position !== void 0) this.position = options.position;
|
|
370
|
+
if (options.width !== void 0) this.width = options.width;
|
|
371
|
+
if (options.height !== void 0) this.height = options.height;
|
|
372
|
+
if (options.allowedFormats !== void 0) this.allowedFormats = options.allowedFormats;
|
|
373
|
+
if (options.defaultContentId !== void 0) this.defaultContentId = options.defaultContentId;
|
|
374
|
+
if (options.metadata !== void 0) this.metadata = options.metadata;
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Check if this is a top-level zone (no parent)
|
|
378
|
+
*/
|
|
379
|
+
isTopLevel() {
|
|
380
|
+
return this.parentId === null;
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Check if this zone has dimensions set
|
|
384
|
+
*/
|
|
385
|
+
hasDimensions() {
|
|
386
|
+
return this.width !== null && this.height !== null;
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Get dimensions as string (e.g., "728x90")
|
|
390
|
+
*/
|
|
391
|
+
getDimensionString() {
|
|
392
|
+
if (!this.hasDimensions()) return null;
|
|
393
|
+
return `${this.width}x${this.height}`;
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Get the parent property
|
|
397
|
+
*/
|
|
398
|
+
async getProperty() {
|
|
399
|
+
const { PropertyCollection } = await Promise.resolve().then(() => Properties_exports);
|
|
400
|
+
return await (await PropertyCollection.create(this.options)).get({ id: this.propertyId });
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Get the full path from root to this zone
|
|
404
|
+
*/
|
|
405
|
+
async getFullPath() {
|
|
406
|
+
return [...(await this.getAncestors()).map((z) => z.name), this.name].join(" > ");
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Get the depth in the hierarchy (0 = top-level)
|
|
410
|
+
*/
|
|
411
|
+
async getDepth() {
|
|
412
|
+
return (await this.getAncestors()).length;
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Create a child zone under this zone
|
|
416
|
+
*/
|
|
417
|
+
async createChild(options) {
|
|
418
|
+
if (!this.id) throw new Error("Zone must be saved before creating children");
|
|
419
|
+
const { ZoneCollection } = await Promise.resolve().then(() => Zones_exports);
|
|
420
|
+
const zone = await (await ZoneCollection.create(this.options)).create({
|
|
421
|
+
...options,
|
|
422
|
+
propertyId: this.propertyId,
|
|
423
|
+
parentId: this.id
|
|
424
|
+
});
|
|
425
|
+
await zone.save();
|
|
426
|
+
return zone;
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Build tree node for this zone and its children
|
|
430
|
+
*/
|
|
431
|
+
async toTreeNode() {
|
|
432
|
+
const children = await this.getChildren();
|
|
433
|
+
const childNodes = await Promise.all(children.map((c) => c.toTreeNode()));
|
|
434
|
+
return {
|
|
435
|
+
zone: this,
|
|
436
|
+
children: childNodes
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Check if a format is allowed in this zone
|
|
441
|
+
*/
|
|
442
|
+
isFormatAllowed(format) {
|
|
443
|
+
if (this.allowedFormats.length === 0) return true;
|
|
444
|
+
return this.allowedFormats.includes(format);
|
|
445
|
+
}
|
|
478
446
|
};
|
|
479
|
-
__decorateClass([
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
async findGlobal() {
|
|
761
|
-
return queryGlobal(this);
|
|
762
|
-
}
|
|
763
|
-
/**
|
|
764
|
-
* Find zones for a tenant plus all global zones.
|
|
765
|
-
*
|
|
766
|
-
* Fails closed if an active tenant context requests a different tenant's
|
|
767
|
-
* rows; the admin/system path keeps the cross-tenant capability. (#1600)
|
|
768
|
-
*
|
|
769
|
-
* @param tenantId - Tenant ID to filter by
|
|
770
|
-
* @returns Array of tenant-specific and global zones
|
|
771
|
-
*/
|
|
772
|
-
async findWithGlobals(tenantId2) {
|
|
773
|
-
return queryWithGlobals(this, tenantId2, "Zone.findWithGlobals");
|
|
774
|
-
}
|
|
775
|
-
}
|
|
776
|
-
const Zones = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
777
|
-
__proto__: null,
|
|
778
|
-
ZoneCollection
|
|
779
|
-
}, Symbol.toStringTag, { value: "Module" }));
|
|
780
|
-
export {
|
|
781
|
-
Property,
|
|
782
|
-
PropertyCollection,
|
|
783
|
-
Zone,
|
|
784
|
-
ZoneCollection,
|
|
785
|
-
promptMessageOptions,
|
|
786
|
-
smrtPropertiesSummarizePrompt
|
|
447
|
+
__decorateClass([tenantId({ nullable: true })], Zone.prototype, "tenantId", 2);
|
|
448
|
+
__decorateClass([foreignKey("Property")], Zone.prototype, "propertyId", 2);
|
|
449
|
+
Zone = __decorateClass([TenantScoped({ mode: "optional" }), smrt({
|
|
450
|
+
api: { include: [
|
|
451
|
+
"list",
|
|
452
|
+
"get",
|
|
453
|
+
"create",
|
|
454
|
+
"update",
|
|
455
|
+
"delete"
|
|
456
|
+
] },
|
|
457
|
+
mcp: { include: [
|
|
458
|
+
"list",
|
|
459
|
+
"get",
|
|
460
|
+
"create"
|
|
461
|
+
] },
|
|
462
|
+
cli: true
|
|
463
|
+
})], Zone);
|
|
464
|
+
//#endregion
|
|
465
|
+
//#region src/collections/Zones.ts
|
|
466
|
+
var Zones_exports = /* @__PURE__ */ __exportAll({ ZoneCollection: () => ZoneCollection });
|
|
467
|
+
var ZoneCollection = class extends SmrtCollection {
|
|
468
|
+
static _itemClass = Zone;
|
|
469
|
+
/**
|
|
470
|
+
* Find all zones for a property
|
|
471
|
+
*
|
|
472
|
+
* @param propertyId - Property ID
|
|
473
|
+
* @returns Array of zones
|
|
474
|
+
*/
|
|
475
|
+
async findByProperty(propertyId) {
|
|
476
|
+
return await this.list({ where: { propertyId } });
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* Find top-level zones for a property (no parent)
|
|
480
|
+
*
|
|
481
|
+
* @param propertyId - Property ID
|
|
482
|
+
* @returns Array of top-level zones
|
|
483
|
+
*/
|
|
484
|
+
async findTopLevel(propertyId) {
|
|
485
|
+
return await this.list({ where: {
|
|
486
|
+
propertyId,
|
|
487
|
+
parentId: null
|
|
488
|
+
} });
|
|
489
|
+
}
|
|
490
|
+
/**
|
|
491
|
+
* Find direct children of a zone
|
|
492
|
+
*
|
|
493
|
+
* @param parentId - Parent zone ID
|
|
494
|
+
* @returns Array of child zones
|
|
495
|
+
*/
|
|
496
|
+
async findChildren(parentId) {
|
|
497
|
+
return await this.list({ where: { parentId } });
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* Find zones by path pattern
|
|
501
|
+
*
|
|
502
|
+
* @param propertyId - Property ID
|
|
503
|
+
* @param path - URL path to match
|
|
504
|
+
* @returns Array of matching zones
|
|
505
|
+
*/
|
|
506
|
+
async findByPath(propertyId, path) {
|
|
507
|
+
return await this.list({ where: {
|
|
508
|
+
propertyId,
|
|
509
|
+
path
|
|
510
|
+
} });
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* Find zones by type
|
|
514
|
+
*
|
|
515
|
+
* @param propertyId - Property ID
|
|
516
|
+
* @param type - Zone type
|
|
517
|
+
* @returns Array of zones of the given type
|
|
518
|
+
*/
|
|
519
|
+
async findByType(propertyId, type) {
|
|
520
|
+
return await this.list({ where: {
|
|
521
|
+
propertyId,
|
|
522
|
+
type
|
|
523
|
+
} });
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* Get the complete zone tree for a property
|
|
527
|
+
*
|
|
528
|
+
* @param propertyId - Property ID
|
|
529
|
+
* @returns ZoneTree structure
|
|
530
|
+
*/
|
|
531
|
+
async getTree(propertyId) {
|
|
532
|
+
const allZones = await this.findByProperty(propertyId);
|
|
533
|
+
const zoneMap = /* @__PURE__ */ new Map();
|
|
534
|
+
const childrenMap = /* @__PURE__ */ new Map();
|
|
535
|
+
for (const zone of allZones) {
|
|
536
|
+
if (zone.id) zoneMap.set(zone.id, zone);
|
|
537
|
+
if (!childrenMap.has(zone.parentId || "")) childrenMap.set(zone.parentId || "", []);
|
|
538
|
+
childrenMap.get(zone.parentId || "")?.push(zone);
|
|
539
|
+
}
|
|
540
|
+
const buildNode = (zone) => {
|
|
541
|
+
return {
|
|
542
|
+
zone,
|
|
543
|
+
children: ((zone.id ? childrenMap.get(zone.id) : []) || []).map(buildNode)
|
|
544
|
+
};
|
|
545
|
+
};
|
|
546
|
+
return {
|
|
547
|
+
propertyId,
|
|
548
|
+
roots: (childrenMap.get("") || []).map(buildNode)
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* Get all ancestors of a zone (path to root)
|
|
553
|
+
*
|
|
554
|
+
* @param zoneId - Zone ID
|
|
555
|
+
* @returns Array of ancestors (from root to parent)
|
|
556
|
+
*/
|
|
557
|
+
async getAncestors(zoneId) {
|
|
558
|
+
const ancestors = [];
|
|
559
|
+
const startZone = await this.get({ id: zoneId });
|
|
560
|
+
if (!startZone) return ancestors;
|
|
561
|
+
let currentId = startZone.parentId;
|
|
562
|
+
while (currentId) {
|
|
563
|
+
const parent = await this.get({ id: currentId });
|
|
564
|
+
if (!parent) break;
|
|
565
|
+
ancestors.unshift(parent);
|
|
566
|
+
currentId = parent.parentId;
|
|
567
|
+
}
|
|
568
|
+
return ancestors;
|
|
569
|
+
}
|
|
570
|
+
/**
|
|
571
|
+
* Get all descendants of a zone recursively
|
|
572
|
+
*
|
|
573
|
+
* @param zoneId - Zone ID
|
|
574
|
+
* @returns Array of all descendant zones
|
|
575
|
+
*/
|
|
576
|
+
async getDescendants(zoneId) {
|
|
577
|
+
const descendants = [];
|
|
578
|
+
const queue = [zoneId];
|
|
579
|
+
while (queue.length > 0) {
|
|
580
|
+
const currentId = queue.shift();
|
|
581
|
+
if (!currentId) continue;
|
|
582
|
+
const children = await this.findChildren(currentId);
|
|
583
|
+
for (const child of children) {
|
|
584
|
+
descendants.push(child);
|
|
585
|
+
if (child.id) queue.push(child.id);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
return descendants;
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* Get the depth of the deepest zone in a property
|
|
592
|
+
*
|
|
593
|
+
* @param propertyId - Property ID
|
|
594
|
+
* @returns Maximum depth (0 = only top-level zones)
|
|
595
|
+
*/
|
|
596
|
+
async getMaxDepth(propertyId) {
|
|
597
|
+
const allZones = await this.findByProperty(propertyId);
|
|
598
|
+
const zonesById = /* @__PURE__ */ new Map();
|
|
599
|
+
for (const zone of allZones) if (zone.id) zonesById.set(zone.id, zone);
|
|
600
|
+
const depthCache = /* @__PURE__ */ new Map();
|
|
601
|
+
const getDepth = (zone) => {
|
|
602
|
+
if (!zone.id) return 0;
|
|
603
|
+
const cached = depthCache.get(zone.id);
|
|
604
|
+
if (cached !== void 0) return cached;
|
|
605
|
+
let depth = 0;
|
|
606
|
+
let current = zone;
|
|
607
|
+
const visited = /* @__PURE__ */ new Set();
|
|
608
|
+
while (current?.id) {
|
|
609
|
+
const parentId = current.parentId;
|
|
610
|
+
if (!parentId) break;
|
|
611
|
+
if (visited.has(parentId)) break;
|
|
612
|
+
visited.add(parentId);
|
|
613
|
+
const parent = zonesById.get(parentId);
|
|
614
|
+
if (!parent) break;
|
|
615
|
+
depth += 1;
|
|
616
|
+
current = parent;
|
|
617
|
+
}
|
|
618
|
+
depthCache.set(zone.id, depth);
|
|
619
|
+
return depth;
|
|
620
|
+
};
|
|
621
|
+
let maxDepth = 0;
|
|
622
|
+
for (const zone of allZones) maxDepth = Math.max(maxDepth, getDepth(zone));
|
|
623
|
+
return maxDepth;
|
|
624
|
+
}
|
|
625
|
+
/**
|
|
626
|
+
* Find zones with specific dimensions
|
|
627
|
+
*
|
|
628
|
+
* @param propertyId - Property ID
|
|
629
|
+
* @param width - Required width
|
|
630
|
+
* @param height - Required height
|
|
631
|
+
* @returns Array of matching zones
|
|
632
|
+
*/
|
|
633
|
+
async findByDimensions(propertyId, width, height) {
|
|
634
|
+
return await this.list({ where: {
|
|
635
|
+
propertyId,
|
|
636
|
+
width,
|
|
637
|
+
height
|
|
638
|
+
} });
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* Find zones that allow a specific format
|
|
642
|
+
*
|
|
643
|
+
* @param propertyId - Property ID
|
|
644
|
+
* @param format - Format to check
|
|
645
|
+
* @returns Array of zones allowing the format
|
|
646
|
+
*/
|
|
647
|
+
async findByAllowedFormat(propertyId, format) {
|
|
648
|
+
return (await this.findByProperty(propertyId)).filter((zone) => zone.isFormatAllowed(format));
|
|
649
|
+
}
|
|
650
|
+
/**
|
|
651
|
+
* Move a zone to a new parent
|
|
652
|
+
*
|
|
653
|
+
* @param zoneId - Zone ID to move
|
|
654
|
+
* @param newParentId - New parent ID (null for top-level)
|
|
655
|
+
* @returns Updated zone
|
|
656
|
+
*/
|
|
657
|
+
async moveZone(zoneId, newParentId) {
|
|
658
|
+
const zone = await this.get({ id: zoneId });
|
|
659
|
+
if (!zone) return null;
|
|
660
|
+
if (newParentId) {
|
|
661
|
+
if ((await this.getDescendants(zoneId)).map((d) => d.id).includes(newParentId)) throw new Error("Cannot move zone into one of its descendants");
|
|
662
|
+
}
|
|
663
|
+
zone.parentId = newParentId;
|
|
664
|
+
await zone.save();
|
|
665
|
+
return zone;
|
|
666
|
+
}
|
|
667
|
+
/**
|
|
668
|
+
* Delete a zone and optionally its descendants
|
|
669
|
+
*
|
|
670
|
+
* @param zoneId - Zone ID to delete
|
|
671
|
+
* @param cascade - Whether to delete descendants
|
|
672
|
+
* @returns Number of zones deleted
|
|
673
|
+
*/
|
|
674
|
+
async deleteZone(zoneId, cascade = false) {
|
|
675
|
+
const zone = await this.get({ id: zoneId });
|
|
676
|
+
if (!zone) return 0;
|
|
677
|
+
let deleted = 0;
|
|
678
|
+
if (cascade) {
|
|
679
|
+
const descendants = await this.getDescendants(zoneId);
|
|
680
|
+
for (const desc of descendants) {
|
|
681
|
+
await desc.delete();
|
|
682
|
+
deleted++;
|
|
683
|
+
}
|
|
684
|
+
} else {
|
|
685
|
+
const children = await this.findChildren(zoneId);
|
|
686
|
+
for (const child of children) {
|
|
687
|
+
child.parentId = zone.parentId;
|
|
688
|
+
await child.save();
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
await zone.delete();
|
|
692
|
+
deleted++;
|
|
693
|
+
return deleted;
|
|
694
|
+
}
|
|
695
|
+
/**
|
|
696
|
+
* Find zones belonging to a specific tenant
|
|
697
|
+
*
|
|
698
|
+
* @param tenantId - Tenant ID to filter by
|
|
699
|
+
* @returns Array of zones for the tenant
|
|
700
|
+
*/
|
|
701
|
+
async findByTenant(tenantId) {
|
|
702
|
+
return this.list({ where: { tenantId } });
|
|
703
|
+
}
|
|
704
|
+
/**
|
|
705
|
+
* Find all global zones (no tenant association).
|
|
706
|
+
*
|
|
707
|
+
* Routes through the shared tenant-global helper so it does not throw under
|
|
708
|
+
* an active tenant context (an explicit `tenant_id IS NULL` filter would be
|
|
709
|
+
* flagged as an isolation violation). (#1600)
|
|
710
|
+
*
|
|
711
|
+
* @returns Array of global zones
|
|
712
|
+
*/
|
|
713
|
+
async findGlobal() {
|
|
714
|
+
return queryGlobal(this);
|
|
715
|
+
}
|
|
716
|
+
/**
|
|
717
|
+
* Find zones for a tenant plus all global zones.
|
|
718
|
+
*
|
|
719
|
+
* Fails closed if an active tenant context requests a different tenant's
|
|
720
|
+
* rows; the admin/system path keeps the cross-tenant capability. (#1600)
|
|
721
|
+
*
|
|
722
|
+
* @param tenantId - Tenant ID to filter by
|
|
723
|
+
* @returns Array of tenant-specific and global zones
|
|
724
|
+
*/
|
|
725
|
+
async findWithGlobals(tenantId) {
|
|
726
|
+
return queryWithGlobals(this, tenantId, "Zone.findWithGlobals");
|
|
727
|
+
}
|
|
787
728
|
};
|
|
788
|
-
//#
|
|
729
|
+
//#endregion
|
|
730
|
+
export { Property, PropertyCollection, Zone, ZoneCollection, promptMessageOptions, smrtPropertiesSummarizePrompt };
|
|
731
|
+
|
|
732
|
+
//# sourceMappingURL=index.js.map
|