@objectstack/plugin-org-scoping 7.0.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.
package/dist/index.mjs ADDED
@@ -0,0 +1,592 @@
1
+ // src/claim-orphan-org-rows.ts
2
+ var SYSTEM_CTX = { isSystem: true };
3
+ function hasOrganizationField(schema) {
4
+ const fields = schema?.fields;
5
+ if (!fields) return false;
6
+ if (Array.isArray(fields)) {
7
+ return fields.some((f) => f?.name === "organization_id");
8
+ }
9
+ return Object.prototype.hasOwnProperty.call(fields, "organization_id");
10
+ }
11
+ async function claimOrphanOrgRows(ql, organizationId, options = {}) {
12
+ const logger = options.logger;
13
+ if (!ql || typeof ql.update !== "function" || typeof ql.find !== "function") {
14
+ return [];
15
+ }
16
+ const registry = ql.registry;
17
+ if (!registry || typeof registry.getAllObjects !== "function") {
18
+ logger?.warn?.("[org-scoping] claimOrphanOrgRows: registry unavailable");
19
+ return [];
20
+ }
21
+ const schemas = registry.getAllObjects();
22
+ const results = [];
23
+ for (const schema of schemas) {
24
+ if (!schema?.name) continue;
25
+ if (schema.managedBy) continue;
26
+ if (schema.name.startsWith("sys_")) continue;
27
+ if (!hasOrganizationField(schema)) continue;
28
+ try {
29
+ const orphans = await ql.find(
30
+ schema.name,
31
+ { where: { organization_id: null }, limit: 1e4, fields: ["id"] },
32
+ { context: SYSTEM_CTX }
33
+ );
34
+ const list = Array.isArray(orphans) ? orphans : Array.isArray(orphans?.records) ? orphans.records : [];
35
+ if (list.length === 0) continue;
36
+ let updated = 0;
37
+ for (const row of list) {
38
+ if (!row?.id) continue;
39
+ try {
40
+ await ql.update(
41
+ schema.name,
42
+ { id: row.id, organization_id: organizationId },
43
+ { context: SYSTEM_CTX }
44
+ );
45
+ updated += 1;
46
+ } catch (e) {
47
+ logger?.warn?.(`[org-scoping] claim failed for ${schema.name}:${row.id}`, {
48
+ error: e.message
49
+ });
50
+ }
51
+ }
52
+ if (updated > 0) {
53
+ results.push({ object: schema.name, count: updated });
54
+ }
55
+ } catch (e) {
56
+ logger?.warn?.(`[org-scoping] claim scan failed for ${schema.name}`, {
57
+ error: e.message
58
+ });
59
+ }
60
+ }
61
+ if (results.length > 0) {
62
+ const total = results.reduce((s, r) => s + r.count, 0);
63
+ logger?.info?.(`[org-scoping] claimed ${total} orphan seed row(s) for organization ${organizationId}`, {
64
+ breakdown: results
65
+ });
66
+ }
67
+ return results;
68
+ }
69
+
70
+ // src/clone-org-seed-data.ts
71
+ var SYSTEM_CTX2 = { isSystem: true };
72
+ var SKIP_COPY_FIELDS = /* @__PURE__ */ new Set([
73
+ "id",
74
+ "created_at",
75
+ "updated_at",
76
+ "organization_id"
77
+ ]);
78
+ var SKIP_COPY_TYPES = /* @__PURE__ */ new Set(["formula", "summary"]);
79
+ function fieldList(schema) {
80
+ const fields = schema?.fields;
81
+ if (!fields) return [];
82
+ if (Array.isArray(fields)) {
83
+ return fields.map((f) => ({
84
+ name: f?.name,
85
+ type: f?.type,
86
+ reference: f?.reference,
87
+ multiple: f?.multiple,
88
+ unique: f?.unique
89
+ }));
90
+ }
91
+ return Object.entries(fields).map(([name, f]) => ({
92
+ name,
93
+ type: f?.type,
94
+ reference: f?.reference,
95
+ multiple: f?.multiple,
96
+ unique: f?.unique
97
+ }));
98
+ }
99
+ function isLookupField(f) {
100
+ return (f.type === "lookup" || f.type === "master_detail" || f.type === "tree") && !!f.reference;
101
+ }
102
+ function hasOrgField(schema) {
103
+ return fieldList(schema).some((f) => f.name === "organization_id");
104
+ }
105
+ function shortId() {
106
+ const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-";
107
+ let out = "";
108
+ for (let i = 0; i < 16; i++) {
109
+ out += alphabet[Math.floor(Math.random() * alphabet.length)];
110
+ }
111
+ return out;
112
+ }
113
+ async function findDonorOrgId(ql) {
114
+ try {
115
+ const res = await ql.find(
116
+ "sys_organization",
117
+ { orderBy: { created_at: "asc" }, limit: 1, fields: ["id"] },
118
+ { context: SYSTEM_CTX2 }
119
+ );
120
+ const list = Array.isArray(res) ? res : Array.isArray(res?.records) ? res.records : [];
121
+ return list[0]?.id ?? null;
122
+ } catch {
123
+ return null;
124
+ }
125
+ }
126
+ async function cloneOrgSeedData(ql, targetOrgId, options = {}) {
127
+ const logger = options.logger;
128
+ if (!ql || typeof ql.find !== "function" || typeof ql.insert !== "function") {
129
+ return [];
130
+ }
131
+ const registry = ql.registry;
132
+ if (!registry || typeof registry.getAllObjects !== "function") {
133
+ logger?.warn?.("[org-scoping] cloneOrgSeedData: registry unavailable");
134
+ return [];
135
+ }
136
+ const donorOrgId = await findDonorOrgId(ql);
137
+ if (!donorOrgId) return [];
138
+ if (donorOrgId === targetOrgId) return [];
139
+ const schemas = registry.getAllObjects().filter(
140
+ (s) => s?.name && !s.managedBy && !s.name.startsWith("sys_") && hasOrgField(s)
141
+ );
142
+ const remap = {};
143
+ const summary = [];
144
+ const inserted = [];
145
+ for (const schema of schemas) {
146
+ const objectName = schema.name;
147
+ try {
148
+ const existing = await ql.find(
149
+ objectName,
150
+ { where: { organization_id: targetOrgId }, limit: 1, fields: ["id"] },
151
+ { context: SYSTEM_CTX2 }
152
+ );
153
+ const existingList = Array.isArray(existing) ? existing : Array.isArray(existing?.records) ? existing.records : [];
154
+ if (existingList.length > 0) {
155
+ continue;
156
+ }
157
+ const donorRows = await ql.find(
158
+ objectName,
159
+ { where: { organization_id: donorOrgId }, limit: 1e4 },
160
+ { context: SYSTEM_CTX2 }
161
+ );
162
+ const rows = Array.isArray(donorRows) ? donorRows : Array.isArray(donorRows?.records) ? donorRows.records : [];
163
+ if (rows.length === 0) continue;
164
+ const fields = fieldList(schema);
165
+ const lookups = fields.filter(isLookupField);
166
+ const uniqueFields = fields.filter((f) => f.unique && !SKIP_COPY_FIELDS.has(f.name));
167
+ const objectRemap = remap[objectName] ?? (remap[objectName] = {});
168
+ let cloned = 0;
169
+ for (const row of rows) {
170
+ const newId = shortId();
171
+ const data = { id: newId, organization_id: targetOrgId };
172
+ for (const f of fields) {
173
+ if (SKIP_COPY_FIELDS.has(f.name)) continue;
174
+ if (f.type && SKIP_COPY_TYPES.has(f.type)) continue;
175
+ if (row[f.name] === void 0) continue;
176
+ data[f.name] = row[f.name];
177
+ }
178
+ const suffix = `+${targetOrgId.slice(-6)}`;
179
+ for (const uf of uniqueFields) {
180
+ const v = data[uf.name];
181
+ if (typeof v !== "string" || !v) continue;
182
+ if (uf.type === "email" && v.includes("@")) {
183
+ const [local, domain] = v.split("@");
184
+ data[uf.name] = `clone-${targetOrgId.slice(-6)}-${local}@${domain}`;
185
+ } else {
186
+ data[uf.name] = `${v}${suffix}`;
187
+ }
188
+ }
189
+ try {
190
+ await ql.insert(objectName, data, { context: SYSTEM_CTX2 });
191
+ objectRemap[row.id] = newId;
192
+ inserted.push({ object: objectName, newId, record: data, lookups });
193
+ cloned++;
194
+ } catch (e) {
195
+ logger?.warn?.("[org-scoping] cloneOrgSeedData: insert failed", {
196
+ object: objectName,
197
+ error: e.message
198
+ });
199
+ }
200
+ }
201
+ if (cloned > 0) summary.push({ object: objectName, count: cloned });
202
+ } catch (e) {
203
+ logger?.warn?.("[org-scoping] cloneOrgSeedData: object failed", {
204
+ object: objectName,
205
+ error: e.message
206
+ });
207
+ }
208
+ }
209
+ for (const item of inserted) {
210
+ if (item.lookups.length === 0) continue;
211
+ const patch = {};
212
+ let dirty = false;
213
+ for (const f of item.lookups) {
214
+ const oldVal = item.record[f.name];
215
+ if (oldVal == null) continue;
216
+ const targetMap = remap[f.reference];
217
+ if (Array.isArray(oldVal)) {
218
+ const next = oldVal.map((v) => typeof v === "string" && targetMap?.[v] || null).filter((v) => v != null);
219
+ if (next.length !== oldVal.length || next.some((v, i) => v !== oldVal[i])) {
220
+ patch[f.name] = next.length > 0 ? next : null;
221
+ dirty = true;
222
+ }
223
+ } else if (typeof oldVal === "string") {
224
+ if (targetMap && targetMap[oldVal]) {
225
+ patch[f.name] = targetMap[oldVal];
226
+ dirty = true;
227
+ } else {
228
+ patch[f.name] = null;
229
+ dirty = true;
230
+ }
231
+ }
232
+ }
233
+ if (!dirty) continue;
234
+ try {
235
+ await ql.update(item.object, { id: item.newId, ...patch }, { context: SYSTEM_CTX2 });
236
+ } catch (e) {
237
+ logger?.warn?.("[org-scoping] cloneOrgSeedData: lookup remap failed", {
238
+ object: item.object,
239
+ id: item.newId,
240
+ error: e.message
241
+ });
242
+ }
243
+ }
244
+ return summary;
245
+ }
246
+
247
+ // src/ensure-default-organization.ts
248
+ var SYSTEM_CTX3 = { isSystem: true };
249
+ async function tryFind(ql, object, where, limit = 100) {
250
+ try {
251
+ const rows = await ql.find(object, { where, limit }, { context: SYSTEM_CTX3 });
252
+ return Array.isArray(rows) ? rows : Array.isArray(rows?.records) ? rows.records : [];
253
+ } catch {
254
+ return [];
255
+ }
256
+ }
257
+ async function tryInsert(ql, object, data) {
258
+ try {
259
+ return await ql.insert(object, data, { context: SYSTEM_CTX3 });
260
+ } catch {
261
+ return null;
262
+ }
263
+ }
264
+ function genId(prefix) {
265
+ const rand = Math.random().toString(36).slice(2, 10);
266
+ const ts = Date.now().toString(36);
267
+ return `${prefix}_${ts}${rand}`;
268
+ }
269
+ async function ensureDefaultOrganization(ql, options = {}) {
270
+ const logger = options.logger;
271
+ if (!ql || typeof ql.find !== "function" || typeof ql.insert !== "function") {
272
+ return { defaultOrgCreated: false, memberCreated: false, reason: "no_admin" };
273
+ }
274
+ const adminPs = await tryFind(ql, "sys_permission_set", { name: "admin_full_access" }, 1);
275
+ if (adminPs.length === 0 || !adminPs[0].id) {
276
+ return { defaultOrgCreated: false, memberCreated: false, reason: "no_admin" };
277
+ }
278
+ const adminPsId = adminPs[0].id;
279
+ const adminGrants = await tryFind(
280
+ ql,
281
+ "sys_user_permission_set",
282
+ { permission_set_id: adminPsId, organization_id: null },
283
+ 50
284
+ );
285
+ if (adminGrants.length === 0) {
286
+ return { defaultOrgCreated: false, memberCreated: false, reason: "no_admin" };
287
+ }
288
+ const sortedGrants = [...adminGrants].sort((a, b) => {
289
+ const ta = a.created_at ? new Date(a.created_at).getTime() : 0;
290
+ const tb = b.created_at ? new Date(b.created_at).getTime() : 0;
291
+ return ta - tb;
292
+ });
293
+ const adminUserId = sortedGrants[0]?.user_id;
294
+ if (!adminUserId) {
295
+ return { defaultOrgCreated: false, memberCreated: false, reason: "no_admin" };
296
+ }
297
+ const memberships = await tryFind(ql, "sys_member", { user_id: adminUserId }, 1);
298
+ if (memberships.length > 0) {
299
+ return {
300
+ defaultOrgCreated: false,
301
+ memberCreated: false,
302
+ reason: "admin_already_in_org"
303
+ };
304
+ }
305
+ let defaultOrgId;
306
+ let defaultOrgCreated = false;
307
+ const existingDefault = await tryFind(ql, "sys_organization", { slug: "default" }, 1);
308
+ if (existingDefault.length > 0 && existingDefault[0].id) {
309
+ defaultOrgId = String(existingDefault[0].id);
310
+ } else {
311
+ const newOrgId = genId("org");
312
+ const orgRow = await tryInsert(ql, "sys_organization", {
313
+ id: newOrgId,
314
+ name: "Default Organization",
315
+ slug: "default",
316
+ logo: null,
317
+ metadata: null
318
+ });
319
+ if (!orgRow) {
320
+ logger?.warn?.("[org-scoping] failed to create default organization for platform admin");
321
+ return { defaultOrgCreated: false, memberCreated: false, reason: "org_insert_failed" };
322
+ }
323
+ defaultOrgId = orgRow?.id ?? newOrgId;
324
+ defaultOrgCreated = true;
325
+ }
326
+ const memRow = await tryInsert(ql, "sys_member", {
327
+ id: genId("mem"),
328
+ organization_id: defaultOrgId,
329
+ user_id: adminUserId,
330
+ role: "owner"
331
+ });
332
+ if (!memRow) {
333
+ logger?.warn?.("[org-scoping] failed to bind platform admin to default organization");
334
+ return {
335
+ defaultOrgCreated,
336
+ defaultOrgId,
337
+ memberCreated: false,
338
+ reason: "member_insert_failed"
339
+ };
340
+ }
341
+ logger?.info?.(
342
+ `[org-scoping] bound platform admin to default organization (${defaultOrgId})`,
343
+ { userId: adminUserId, defaultOrgId }
344
+ );
345
+ return { defaultOrgCreated, defaultOrgId, memberCreated: true };
346
+ }
347
+
348
+ // src/manifest.ts
349
+ var ORG_SCOPING_PLUGIN_ID = "com.objectstack.plugin-org-scoping";
350
+ var ORG_SCOPING_PLUGIN_VERSION = "1.0.0";
351
+ var orgScopingObjects = [];
352
+ var orgScopingPluginManifestHeader = {
353
+ id: ORG_SCOPING_PLUGIN_ID,
354
+ namespace: "sys",
355
+ version: ORG_SCOPING_PLUGIN_VERSION,
356
+ type: "plugin",
357
+ scope: "system",
358
+ defaultDatasource: "cloud",
359
+ name: "Organization Scoping Plugin",
360
+ description: "Row-level Organization isolation: auto-stamps `organization_id` on insert from `ExecutionContext.tenantId`, replays seed datasets per new org, and bootstraps a default organization for the first platform admin."
361
+ };
362
+
363
+ // src/org-scoping-plugin.ts
364
+ var OrgScopingPlugin = class {
365
+ constructor(options = {}) {
366
+ this.name = "com.objectstack.org-scoping";
367
+ this.type = "standard";
368
+ this.version = "1.0.0";
369
+ this.dependencies = ["com.objectstack.engine.objectql"];
370
+ /** Per-object field-name cache; same shape as SecurityPlugin's. */
371
+ this.fieldNamesCache = /* @__PURE__ */ new Map();
372
+ this.opts = {
373
+ ensureDefaultOrganization: options.ensureDefaultOrganization !== false
374
+ };
375
+ }
376
+ async init(ctx) {
377
+ ctx.logger.info("Initializing Org-Scoping Plugin...");
378
+ ctx.registerService("org-scoping", this);
379
+ ctx.getService("manifest").register({
380
+ ...orgScopingPluginManifestHeader,
381
+ objects: orgScopingObjects
382
+ });
383
+ ctx.logger.info("Org-Scoping Plugin initialized");
384
+ }
385
+ async start(ctx) {
386
+ ctx.logger.info("Starting Org-Scoping Plugin...");
387
+ let ql;
388
+ let metadata;
389
+ try {
390
+ ql = ctx.getService("objectql");
391
+ try {
392
+ metadata = ctx.getService("metadata");
393
+ } catch {
394
+ metadata = void 0;
395
+ }
396
+ } catch {
397
+ ctx.logger.warn(
398
+ "ObjectQL service not available, org-scoping middleware not registered"
399
+ );
400
+ return;
401
+ }
402
+ if (!ql || typeof ql.registerMiddleware !== "function") {
403
+ ctx.logger.warn(
404
+ "ObjectQL engine does not support middleware, org-scoping middleware not registered"
405
+ );
406
+ return;
407
+ }
408
+ ql.registerMiddleware(async (opCtx, next) => {
409
+ if (opCtx.context?.isSystem) return next();
410
+ if (opCtx.operation === "insert" && opCtx.data && typeof opCtx.data === "object" && !Array.isArray(opCtx.data) && opCtx.context?.tenantId) {
411
+ const fields = await this.getObjectFieldNames(metadata, opCtx.object, ql);
412
+ if (fields && fields.has("organization_id")) {
413
+ const data = opCtx.data;
414
+ if (data.organization_id == null || data.organization_id === "") {
415
+ data.organization_id = opCtx.context.tenantId;
416
+ }
417
+ }
418
+ }
419
+ await next();
420
+ });
421
+ ql.registerMiddleware(async (opCtx, next) => {
422
+ await next();
423
+ if (opCtx?.object !== "sys_organization" || opCtx?.operation !== "create" && opCtx?.operation !== "insert") {
424
+ return;
425
+ }
426
+ const newOrgId = opCtx?.result?.id ?? opCtx?.data?.id;
427
+ if (!newOrgId) return;
428
+ const kernel = ctx.kernel ?? ctx;
429
+ let datasets;
430
+ try {
431
+ const raw = kernel?.getService?.("seed-datasets");
432
+ if (Array.isArray(raw) && raw.length > 0) datasets = raw;
433
+ } catch {
434
+ }
435
+ let orgCount = 0;
436
+ try {
437
+ const allOrgs = await ql.find(
438
+ "sys_organization",
439
+ { limit: 2, fields: ["id"] },
440
+ { context: { isSystem: true } }
441
+ );
442
+ const list = Array.isArray(allOrgs) ? allOrgs : Array.isArray(allOrgs?.records) ? allOrgs.records : [];
443
+ orgCount = list.length;
444
+ } catch (e) {
445
+ ctx.logger.warn("[org-scoping] failed to count organizations", {
446
+ error: e.message
447
+ });
448
+ }
449
+ let replayed = false;
450
+ try {
451
+ const replayer = kernel?.getService?.("seed-replayer");
452
+ if (typeof replayer === "function") {
453
+ const summary = await replayer(newOrgId);
454
+ const total = (summary?.inserted ?? 0) + (summary?.updated ?? 0);
455
+ ctx.logger.info(
456
+ `[org-scoping] per-org seed replay for ${newOrgId}: +${summary?.inserted ?? 0} inserted, ${summary?.updated ?? 0} updated, ${summary?.errors?.length ?? 0} error(s)`,
457
+ {
458
+ organizationId: newOrgId,
459
+ errors: summary?.errors?.slice?.(0, 5)
460
+ }
461
+ );
462
+ if (total > 0) replayed = true;
463
+ } else if (datasets) {
464
+ ctx.logger.warn(
465
+ "[org-scoping] per-org seed: datasets present but no replayer registered",
466
+ { organizationId: newOrgId }
467
+ );
468
+ }
469
+ } catch (e) {
470
+ ctx.logger.warn(
471
+ "[org-scoping] per-org seed replay failed, falling back",
472
+ { organizationId: newOrgId, error: e.message }
473
+ );
474
+ }
475
+ if (replayed) return;
476
+ if (orgCount === 1) {
477
+ try {
478
+ const claims = await claimOrphanOrgRows(ql, newOrgId, { logger: ctx.logger });
479
+ if (claims.length > 0) {
480
+ const total = claims.reduce((s, c) => s + c.count, 0);
481
+ ctx.logger.info(
482
+ `[org-scoping] claimed ${total} orphan seed row(s) for first organization ${newOrgId}`,
483
+ { breakdown: claims }
484
+ );
485
+ return;
486
+ }
487
+ } catch (e) {
488
+ ctx.logger.warn("[org-scoping] claim-orphan-org-rows failed", {
489
+ error: e.message
490
+ });
491
+ }
492
+ }
493
+ if (orgCount > 1) {
494
+ try {
495
+ const summary = await cloneOrgSeedData(ql, newOrgId, { logger: ctx.logger });
496
+ if (summary.length > 0) {
497
+ const total = summary.reduce((s, c) => s + c.count, 0);
498
+ ctx.logger.info(
499
+ `[org-scoping] cloned ${total} seed row(s) for new organization ${newOrgId}`,
500
+ { breakdown: summary }
501
+ );
502
+ }
503
+ } catch (e) {
504
+ ctx.logger.warn("[org-scoping] clone-org-seed-data failed", {
505
+ organizationId: newOrgId,
506
+ error: e.message
507
+ });
508
+ }
509
+ }
510
+ });
511
+ if (this.opts.ensureDefaultOrganization) {
512
+ const runEnsure = async () => {
513
+ try {
514
+ const res = await ensureDefaultOrganization(ql, { logger: ctx.logger });
515
+ if (res.defaultOrgCreated) {
516
+ ctx.logger.info(
517
+ `[org-scoping] created Default Organization ${res.defaultOrgId} for platform admin`
518
+ );
519
+ }
520
+ } catch (e) {
521
+ ctx.logger.warn?.("[org-scoping] ensureDefaultOrganization failed", {
522
+ error: e.message
523
+ });
524
+ }
525
+ };
526
+ if (typeof ctx.hook === "function") {
527
+ ctx.hook("kernel:ready", runEnsure);
528
+ } else {
529
+ void runEnsure();
530
+ }
531
+ ql.registerMiddleware(async (opCtx, next) => {
532
+ await next();
533
+ if (opCtx?.object === "sys_user_permission_set" && (opCtx?.operation === "insert" || opCtx?.operation === "create")) {
534
+ await runEnsure();
535
+ }
536
+ });
537
+ }
538
+ ctx.logger.info("Org-Scoping middleware registered on ObjectQL engine");
539
+ }
540
+ async destroy() {
541
+ }
542
+ /**
543
+ * Resolve the column-name set for an object (mirrors SecurityPlugin's
544
+ * loader so the two plugins behave consistently). Returns `null` if
545
+ * the schema can't be loaded — caller skips injection.
546
+ */
547
+ async getObjectFieldNames(metadata, objectName, ql) {
548
+ if (this.fieldNamesCache.has(objectName)) {
549
+ return this.fieldNamesCache.get(objectName) ?? null;
550
+ }
551
+ const result = await this.loadObjectFieldNames(metadata, objectName, ql);
552
+ if (result) this.fieldNamesCache.set(objectName, result);
553
+ return result;
554
+ }
555
+ async loadObjectFieldNames(metadata, objectName, ql) {
556
+ try {
557
+ let obj = typeof ql?.getSchema === "function" ? ql.getSchema(objectName) : null;
558
+ if (!obj || !obj.fields) {
559
+ obj = await metadata?.get?.("object", objectName);
560
+ }
561
+ if (!obj || !obj.fields) return null;
562
+ const set = /* @__PURE__ */ new Set(["id"]);
563
+ if (Array.isArray(obj.fields)) {
564
+ for (const f of obj.fields) {
565
+ if (f?.name) set.add(String(f.name));
566
+ }
567
+ } else if (typeof obj.fields === "object") {
568
+ for (const key of Object.keys(obj.fields)) {
569
+ set.add(key);
570
+ const v = obj.fields[key];
571
+ if (v && typeof v === "object" && v.name) set.add(String(v.name));
572
+ }
573
+ } else {
574
+ return null;
575
+ }
576
+ return set;
577
+ } catch {
578
+ return null;
579
+ }
580
+ }
581
+ };
582
+ export {
583
+ ORG_SCOPING_PLUGIN_ID,
584
+ ORG_SCOPING_PLUGIN_VERSION,
585
+ OrgScopingPlugin,
586
+ claimOrphanOrgRows,
587
+ cloneOrgSeedData,
588
+ ensureDefaultOrganization,
589
+ orgScopingObjects,
590
+ orgScopingPluginManifestHeader
591
+ };
592
+ //# sourceMappingURL=index.mjs.map