@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/LICENSE ADDED
@@ -0,0 +1,93 @@
1
+ License text copyright (c) 2020 MariaDB Corporation Ab, All Rights Reserved.
2
+ "Business Source License" is a trademark of MariaDB Corporation Ab.
3
+
4
+ Parameters
5
+
6
+ Licensor: ObjectStack AI LLC
7
+ Licensed Work: ObjectStack Runtime: the BSL-licensed packages
8
+ of the ObjectStack monorepo as listed in LICENSING.md.
9
+ Copyright (c) 2026 ObjectStack AI LLC.
10
+ Additional Use Grant: You may make production use of the Licensed Work, provided
11
+ Your use does not include offering the Licensed Work to third
12
+ parties on a hosted or embedded basis in order to compete with
13
+ ObjectStack AI LLC's paid version(s) of the Licensed Work. For purposes
14
+ of this license:
15
+
16
+ A "competitive offering" is a Product that is offered to third
17
+ parties on a paid basis, including through paid support
18
+ arrangements, that significantly overlaps with the capabilities
19
+ of ObjectStack AI LLC's paid version(s) of the Licensed Work. If Your
20
+ Product is not a competitive offering when You first make it
21
+ generally available, it will not become a competitive offering
22
+ later due to ObjectStack AI LLC releasing a new version of the Licensed
23
+ Work with additional capabilities. In addition, Products that
24
+ are not provided on a paid basis are not competitive.
25
+
26
+ "Product" means software that is offered to end users to manage
27
+ in their own environments or offered as a service on a hosted
28
+ basis.
29
+
30
+ "Embedded" means including the source code or executable code
31
+ from the Licensed Work in a competitive offering. "Embedded"
32
+ also means packaging the competitive offering in such a way
33
+ that the Licensed Work must be accessed or downloaded for the
34
+ competitive offering to operate.
35
+
36
+ Hosting or using the Licensed Work(s) for internal purposes
37
+ within an organization is not considered a competitive
38
+ offering. ObjectStack AI LLC considers your organization to include all
39
+ of your affiliates under common control.
40
+
41
+ For binding interpretive guidance on using ObjectStack AI LLC products
42
+ under the Business Source License, please visit our FAQ.
43
+ (see LICENSING.md in this repository)
44
+ Change Date: Four years from the date the Licensed Work is published.
45
+ Change License: Apache License, Version 2.0
46
+
47
+ For information about alternative licensing arrangements for the Licensed Work,
48
+ please contact licensing@objectstack.dev.
49
+
50
+ Notice
51
+
52
+ Business Source License 1.1
53
+
54
+ Terms
55
+
56
+ The Licensor hereby grants you the right to copy, modify, create derivative
57
+ works, redistribute, and make non-production use of the Licensed Work. The
58
+ Licensor may make an Additional Use Grant, above, permitting limited production use.
59
+
60
+ Effective on the Change Date, or the fourth anniversary of the first publicly
61
+ available distribution of a specific version of the Licensed Work under this
62
+ License, whichever comes first, the Licensor hereby grants you rights under
63
+ the terms of the Change License, and the rights granted in the paragraph
64
+ above terminate.
65
+
66
+ If your use of the Licensed Work does not comply with the requirements
67
+ currently in effect as described in this License, you must purchase a
68
+ commercial license from the Licensor, its affiliated entities, or authorized
69
+ resellers, or you must refrain from using the Licensed Work.
70
+
71
+ All copies of the original and modified Licensed Work, and derivative works
72
+ of the Licensed Work, are subject to this License. This License applies
73
+ separately for each version of the Licensed Work and the Change Date may vary
74
+ for each version of the Licensed Work released by Licensor.
75
+
76
+ You must conspicuously display this License on each original or modified copy
77
+ of the Licensed Work. If you receive the Licensed Work in original or
78
+ modified form from a third party, the terms and conditions set forth in this
79
+ License apply to your use of that work.
80
+
81
+ Any use of the Licensed Work in violation of this License will automatically
82
+ terminate your rights under this License for the current and all other
83
+ versions of the Licensed Work.
84
+
85
+ This License does not grant you any right in any trademark or logo of
86
+ Licensor or its affiliates (provided that you may use a trademark or logo of
87
+ Licensor as expressly required by this License).
88
+
89
+ TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON
90
+ AN "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS,
91
+ EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF
92
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND
93
+ TITLE.
package/README.md ADDED
@@ -0,0 +1,55 @@
1
+ # @objectstack/plugin-org-scoping
2
+
3
+ > Row-level **Organization** isolation for ObjectStack — the LOGICAL multi-tenant building block.
4
+
5
+ `@objectstack/plugin-org-scoping` makes `sys_organization` a first-class row-level scope:
6
+
7
+ - **Insert auto-stamp** — fills `organization_id` from `ExecutionContext.tenantId` on every authenticated insert (when the target object declares the column).
8
+ - **Per-org seed replay** — every `sys_organization` insert triggers a copy of the app's demo dataset into the new org (via `seed-replayer`, or fallback `claimOrphanOrgRows` / `cloneOrgSeedData`).
9
+ - **Default-org bootstrap** — the first platform admin gets a `Default Organization` (slug `default`) bound as `owner` on `kernel:ready`, so the dashboard isn't empty after first sign-up.
10
+
11
+ Pair with [`@objectstack/plugin-security`](../plugin-security/README.md) for full multi-tenant RBAC + RLS + Field-Level Security. Standalone install gives a single-tenant deployment.
12
+
13
+ ## Naming
14
+
15
+ The word "tenant" in ObjectStack means **physical** isolation (one Environment = one database, per ADR-0002 and `@objectstack/driver-turso`'s multi-tenant router). This plugin is about **logical** row-level scoping inside a single database — orthogonal to physical tenancy. Hence "org-scoping", not "multi-tenant".
16
+
17
+ ## Install
18
+
19
+ ```bash
20
+ pnpm add @objectstack/plugin-org-scoping @objectstack/plugin-security
21
+ ```
22
+
23
+ ## Usage
24
+
25
+ ```ts
26
+ import { OrgScopingPlugin } from '@objectstack/plugin-org-scoping';
27
+ import { SecurityPlugin } from '@objectstack/plugin-security';
28
+
29
+ // OrgScopingPlugin MUST be registered BEFORE SecurityPlugin — the
30
+ // latter probes `getService('org-scoping')` at start time to decide
31
+ // whether to keep wildcard `current_user.organization_id` RLS policies.
32
+ await kernel.use(new OrgScopingPlugin());
33
+ await kernel.use(new SecurityPlugin());
34
+ ```
35
+
36
+ Or via the `OS_MULTI_TENANT` env switch when using `@objectstack/runtime` / `@objectstack/plugin-dev`:
37
+
38
+ ```bash
39
+ OS_MULTI_TENANT=true objectstack serve
40
+ ```
41
+
42
+ ## Options
43
+
44
+ ```ts
45
+ new OrgScopingPlugin({
46
+ ensureDefaultOrganization: true, // default — auto-create slug="default" for first admin
47
+ });
48
+ ```
49
+
50
+ Set `ensureDefaultOrganization: false` to fully self-manage onboarding via invitations / a custom UI.
51
+
52
+ ## See also
53
+
54
+ - ADR-0002 — Physical multi-tenancy & driver-turso router
55
+ - `@objectstack/plugin-security` — RBAC, RLS, Field-Level Security
@@ -0,0 +1,190 @@
1
+ import { Plugin, PluginContext } from '@objectstack/core';
2
+
3
+ interface OrgScopingPluginOptions {
4
+ /**
5
+ * Whether to auto-create a `Default Organization` (slug `default`)
6
+ * and bind the first platform admin as `owner` when they have zero
7
+ * memberships. Set to `false` for deployments that fully self-manage
8
+ * org provisioning via invitation links or a custom onboarding flow.
9
+ *
10
+ * @default true
11
+ */
12
+ ensureDefaultOrganization?: boolean;
13
+ }
14
+ /**
15
+ * OrgScopingPlugin
16
+ *
17
+ * Makes `sys_organization` a first-class row-level isolation boundary:
18
+ *
19
+ * 1. **insert auto-stamp** — on every authenticated `insert` whose
20
+ * target object declares `organization_id`, fill the column from
21
+ * `ExecutionContext.tenantId`. Without this, freshly-created
22
+ * rows have `organization_id = NULL` and the default
23
+ * `tenant_isolation` RLS policy hides them from the very user
24
+ * who just created them.
25
+ *
26
+ * 2. **per-org seed replay** — after `sys_organization` insert, copy
27
+ * the artifact's demo seed data into the new org. Three paths
28
+ * (in order of preference):
29
+ * a. replay registered `seed-datasets` via the kernel-level
30
+ * `seed-replayer` callable (set by AppPlugin),
31
+ * b. for the FIRST org, `claimOrphanOrgRows` adopts any
32
+ * NULL-org rows a previous inline-seed may have inserted,
33
+ * c. for SUBSEQUENT orgs, `cloneOrgSeedData` shallow-clones
34
+ * rows from the very first org (donor-pattern).
35
+ *
36
+ * 3. **default-org bootstrap** — on `kernel:ready` and after every
37
+ * `sys_user_permission_set` insert, ensure the platform admin has
38
+ * a Default Organization to operate in (idempotent on slug
39
+ * `default` + admin's existing memberships).
40
+ *
41
+ * Why split from plugin-security:
42
+ * - plugin-security is a single-tenant-aware RBAC + RLS engine; it
43
+ * should not know about Organization-specific seed flows.
44
+ * - This plugin is purely opt-in: not installing it gives a
45
+ * single-tenant deployment (no `organization_id` injection, no
46
+ * per-org seed clone, no default-org bootstrap). plugin-security
47
+ * detects its presence via `getService('org-scoping')` and adjusts
48
+ * RLS policy stripping accordingly.
49
+ *
50
+ * Naming note: "org-scoping" deliberately avoids the word "tenant"
51
+ * because in ObjectStack "tenant" already means *physical isolation*
52
+ * (one Environment = one database, per ADR-0002 and driver-turso's
53
+ * multi-tenant router). This plugin is about LOGICAL row-level
54
+ * scoping inside a single database — orthogonal to physical tenancy.
55
+ *
56
+ * Dependencies:
57
+ * - `objectql` (engine middleware host)
58
+ */
59
+ declare class OrgScopingPlugin implements Plugin {
60
+ name: string;
61
+ type: string;
62
+ version: string;
63
+ dependencies: string[];
64
+ /** Per-object field-name cache; same shape as SecurityPlugin's. */
65
+ private readonly fieldNamesCache;
66
+ private readonly opts;
67
+ constructor(options?: OrgScopingPluginOptions);
68
+ init(ctx: PluginContext): Promise<void>;
69
+ start(ctx: PluginContext): Promise<void>;
70
+ destroy(): Promise<void>;
71
+ /**
72
+ * Resolve the column-name set for an object (mirrors SecurityPlugin's
73
+ * loader so the two plugins behave consistently). Returns `null` if
74
+ * the schema can't be loaded — caller skips injection.
75
+ */
76
+ private getObjectFieldNames;
77
+ private loadObjectFieldNames;
78
+ }
79
+
80
+ interface ClaimOptions {
81
+ logger?: {
82
+ info: (message: string, meta?: Record<string, any>) => void;
83
+ warn: (message: string, meta?: Record<string, any>) => void;
84
+ };
85
+ }
86
+ /**
87
+ * Assign every orphaned seed row to `organizationId`.
88
+ *
89
+ * Walks `ql.registry.getAllObjects()`, filters to schemas that
90
+ * (a) are not `managedBy` (skip sys_/auth/platform tables),
91
+ * (b) declare an `organization_id` field,
92
+ * and runs an `update(where: { organization_id: null }, patch: {
93
+ * organization_id: organizationId })` against each as `isSystem`.
94
+ *
95
+ * Returns a per-object summary `{ object, count }[]`.
96
+ */
97
+ declare function claimOrphanOrgRows(ql: any, organizationId: string, options?: ClaimOptions): Promise<{
98
+ object: string;
99
+ count: number;
100
+ }[]>;
101
+
102
+ interface CloneOptions {
103
+ logger?: {
104
+ info: (message: string, meta?: Record<string, any>) => void;
105
+ warn: (message: string, meta?: Record<string, any>) => void;
106
+ };
107
+ }
108
+ declare function cloneOrgSeedData(ql: any, targetOrgId: string, options?: CloneOptions): Promise<{
109
+ object: string;
110
+ count: number;
111
+ }[]>;
112
+
113
+ /**
114
+ * ensureDefaultOrganization — multi-tenant bootstrap helper.
115
+ *
116
+ * In multi-tenant deployments the freshly-promoted platform admin
117
+ * (`admin_full_access` granted with `organization_id IS NULL`) needs
118
+ * at least one `sys_organization` to carry an `activeOrganizationId`
119
+ * on their session. Without it, the default `tenant_isolation` RLS
120
+ * policy filters everything to zero rows and the admin sees an empty
121
+ * console even though they have full access.
122
+ *
123
+ * Strategy (idempotent, run on `kernel:ready` and after every
124
+ * `sys_user_permission_set` insert):
125
+ *
126
+ * 1. Find the platform admin (oldest `sys_user_permission_set` row
127
+ * with `permission_set_id = admin_full_access` and
128
+ * `organization_id IS NULL`). If none, no-op.
129
+ * 2. If that user already has any `sys_member` row, no-op (they
130
+ * either created their own org or were invited into one — we
131
+ * respect that and never auto-create a "Default Organization"
132
+ * behind their back).
133
+ * 3. Re-use a pre-existing `slug='default'` org if present;
134
+ * otherwise create one. Stable slug keeps human-readable URLs
135
+ * predictable across cold-boots.
136
+ * 4. Insert a `sys_member { role: 'owner' }` linking the admin to
137
+ * the default org.
138
+ *
139
+ * This is the ONLY framework-side auto-provisioning of an org.
140
+ * Subsequent users must accept an invitation or explicitly create
141
+ * their first organization — `claimOrphanOrgRows` / `cloneOrgSeedData`
142
+ * handle the seed-data side for those flows.
143
+ */
144
+ interface EnsureOptions {
145
+ logger?: {
146
+ info: (message: string, meta?: Record<string, any>) => void;
147
+ warn: (message: string, meta?: Record<string, any>) => void;
148
+ };
149
+ }
150
+ interface EnsureDefaultOrganizationResult {
151
+ /** Whether a brand-new org row was inserted (vs. re-using slug=default). */
152
+ defaultOrgCreated: boolean;
153
+ /** Resolved (or freshly minted) default-org id; undefined when no admin exists yet. */
154
+ defaultOrgId?: string;
155
+ /** Whether a sys_member row was inserted binding the admin to the default org. */
156
+ memberCreated: boolean;
157
+ /** Human-readable reason when the helper short-circuited. */
158
+ reason?: 'no_admin' | 'admin_already_in_org' | 'org_insert_failed' | 'member_insert_failed';
159
+ }
160
+ /**
161
+ * Ensure the platform admin has a Default Organization to operate in.
162
+ * Safe to call multiple times — idempotent on stable slug `default`
163
+ * and on the presence of any existing `sys_member` row for the admin.
164
+ */
165
+ declare function ensureDefaultOrganization(ql: any, options?: EnsureOptions): Promise<EnsureDefaultOrganizationResult>;
166
+
167
+ /**
168
+ * Canonical plugin-org-scoping manifest source.
169
+ *
170
+ * Imported by `objectstack.config.ts` (compile-time) and
171
+ * `org-scoping-plugin.ts` (runtime `manifest.register`) so the two
172
+ * registration paths cannot drift.
173
+ */
174
+ declare const ORG_SCOPING_PLUGIN_ID = "com.objectstack.plugin-org-scoping";
175
+ declare const ORG_SCOPING_PLUGIN_VERSION = "1.0.0";
176
+ /** This plugin owns no `sys_*` objects — Organization itself lives in `@objectstack/platform-objects`. */
177
+ declare const orgScopingObjects: readonly [];
178
+ /** Manifest header shared by compile-time config and runtime registration. */
179
+ declare const orgScopingPluginManifestHeader: {
180
+ id: string;
181
+ namespace: string;
182
+ version: string;
183
+ type: "plugin";
184
+ scope: "system";
185
+ defaultDatasource: string;
186
+ name: string;
187
+ description: string;
188
+ };
189
+
190
+ export { type EnsureDefaultOrganizationResult, ORG_SCOPING_PLUGIN_ID, ORG_SCOPING_PLUGIN_VERSION, OrgScopingPlugin, type OrgScopingPluginOptions, claimOrphanOrgRows, cloneOrgSeedData, ensureDefaultOrganization, orgScopingObjects, orgScopingPluginManifestHeader };
@@ -0,0 +1,190 @@
1
+ import { Plugin, PluginContext } from '@objectstack/core';
2
+
3
+ interface OrgScopingPluginOptions {
4
+ /**
5
+ * Whether to auto-create a `Default Organization` (slug `default`)
6
+ * and bind the first platform admin as `owner` when they have zero
7
+ * memberships. Set to `false` for deployments that fully self-manage
8
+ * org provisioning via invitation links or a custom onboarding flow.
9
+ *
10
+ * @default true
11
+ */
12
+ ensureDefaultOrganization?: boolean;
13
+ }
14
+ /**
15
+ * OrgScopingPlugin
16
+ *
17
+ * Makes `sys_organization` a first-class row-level isolation boundary:
18
+ *
19
+ * 1. **insert auto-stamp** — on every authenticated `insert` whose
20
+ * target object declares `organization_id`, fill the column from
21
+ * `ExecutionContext.tenantId`. Without this, freshly-created
22
+ * rows have `organization_id = NULL` and the default
23
+ * `tenant_isolation` RLS policy hides them from the very user
24
+ * who just created them.
25
+ *
26
+ * 2. **per-org seed replay** — after `sys_organization` insert, copy
27
+ * the artifact's demo seed data into the new org. Three paths
28
+ * (in order of preference):
29
+ * a. replay registered `seed-datasets` via the kernel-level
30
+ * `seed-replayer` callable (set by AppPlugin),
31
+ * b. for the FIRST org, `claimOrphanOrgRows` adopts any
32
+ * NULL-org rows a previous inline-seed may have inserted,
33
+ * c. for SUBSEQUENT orgs, `cloneOrgSeedData` shallow-clones
34
+ * rows from the very first org (donor-pattern).
35
+ *
36
+ * 3. **default-org bootstrap** — on `kernel:ready` and after every
37
+ * `sys_user_permission_set` insert, ensure the platform admin has
38
+ * a Default Organization to operate in (idempotent on slug
39
+ * `default` + admin's existing memberships).
40
+ *
41
+ * Why split from plugin-security:
42
+ * - plugin-security is a single-tenant-aware RBAC + RLS engine; it
43
+ * should not know about Organization-specific seed flows.
44
+ * - This plugin is purely opt-in: not installing it gives a
45
+ * single-tenant deployment (no `organization_id` injection, no
46
+ * per-org seed clone, no default-org bootstrap). plugin-security
47
+ * detects its presence via `getService('org-scoping')` and adjusts
48
+ * RLS policy stripping accordingly.
49
+ *
50
+ * Naming note: "org-scoping" deliberately avoids the word "tenant"
51
+ * because in ObjectStack "tenant" already means *physical isolation*
52
+ * (one Environment = one database, per ADR-0002 and driver-turso's
53
+ * multi-tenant router). This plugin is about LOGICAL row-level
54
+ * scoping inside a single database — orthogonal to physical tenancy.
55
+ *
56
+ * Dependencies:
57
+ * - `objectql` (engine middleware host)
58
+ */
59
+ declare class OrgScopingPlugin implements Plugin {
60
+ name: string;
61
+ type: string;
62
+ version: string;
63
+ dependencies: string[];
64
+ /** Per-object field-name cache; same shape as SecurityPlugin's. */
65
+ private readonly fieldNamesCache;
66
+ private readonly opts;
67
+ constructor(options?: OrgScopingPluginOptions);
68
+ init(ctx: PluginContext): Promise<void>;
69
+ start(ctx: PluginContext): Promise<void>;
70
+ destroy(): Promise<void>;
71
+ /**
72
+ * Resolve the column-name set for an object (mirrors SecurityPlugin's
73
+ * loader so the two plugins behave consistently). Returns `null` if
74
+ * the schema can't be loaded — caller skips injection.
75
+ */
76
+ private getObjectFieldNames;
77
+ private loadObjectFieldNames;
78
+ }
79
+
80
+ interface ClaimOptions {
81
+ logger?: {
82
+ info: (message: string, meta?: Record<string, any>) => void;
83
+ warn: (message: string, meta?: Record<string, any>) => void;
84
+ };
85
+ }
86
+ /**
87
+ * Assign every orphaned seed row to `organizationId`.
88
+ *
89
+ * Walks `ql.registry.getAllObjects()`, filters to schemas that
90
+ * (a) are not `managedBy` (skip sys_/auth/platform tables),
91
+ * (b) declare an `organization_id` field,
92
+ * and runs an `update(where: { organization_id: null }, patch: {
93
+ * organization_id: organizationId })` against each as `isSystem`.
94
+ *
95
+ * Returns a per-object summary `{ object, count }[]`.
96
+ */
97
+ declare function claimOrphanOrgRows(ql: any, organizationId: string, options?: ClaimOptions): Promise<{
98
+ object: string;
99
+ count: number;
100
+ }[]>;
101
+
102
+ interface CloneOptions {
103
+ logger?: {
104
+ info: (message: string, meta?: Record<string, any>) => void;
105
+ warn: (message: string, meta?: Record<string, any>) => void;
106
+ };
107
+ }
108
+ declare function cloneOrgSeedData(ql: any, targetOrgId: string, options?: CloneOptions): Promise<{
109
+ object: string;
110
+ count: number;
111
+ }[]>;
112
+
113
+ /**
114
+ * ensureDefaultOrganization — multi-tenant bootstrap helper.
115
+ *
116
+ * In multi-tenant deployments the freshly-promoted platform admin
117
+ * (`admin_full_access` granted with `organization_id IS NULL`) needs
118
+ * at least one `sys_organization` to carry an `activeOrganizationId`
119
+ * on their session. Without it, the default `tenant_isolation` RLS
120
+ * policy filters everything to zero rows and the admin sees an empty
121
+ * console even though they have full access.
122
+ *
123
+ * Strategy (idempotent, run on `kernel:ready` and after every
124
+ * `sys_user_permission_set` insert):
125
+ *
126
+ * 1. Find the platform admin (oldest `sys_user_permission_set` row
127
+ * with `permission_set_id = admin_full_access` and
128
+ * `organization_id IS NULL`). If none, no-op.
129
+ * 2. If that user already has any `sys_member` row, no-op (they
130
+ * either created their own org or were invited into one — we
131
+ * respect that and never auto-create a "Default Organization"
132
+ * behind their back).
133
+ * 3. Re-use a pre-existing `slug='default'` org if present;
134
+ * otherwise create one. Stable slug keeps human-readable URLs
135
+ * predictable across cold-boots.
136
+ * 4. Insert a `sys_member { role: 'owner' }` linking the admin to
137
+ * the default org.
138
+ *
139
+ * This is the ONLY framework-side auto-provisioning of an org.
140
+ * Subsequent users must accept an invitation or explicitly create
141
+ * their first organization — `claimOrphanOrgRows` / `cloneOrgSeedData`
142
+ * handle the seed-data side for those flows.
143
+ */
144
+ interface EnsureOptions {
145
+ logger?: {
146
+ info: (message: string, meta?: Record<string, any>) => void;
147
+ warn: (message: string, meta?: Record<string, any>) => void;
148
+ };
149
+ }
150
+ interface EnsureDefaultOrganizationResult {
151
+ /** Whether a brand-new org row was inserted (vs. re-using slug=default). */
152
+ defaultOrgCreated: boolean;
153
+ /** Resolved (or freshly minted) default-org id; undefined when no admin exists yet. */
154
+ defaultOrgId?: string;
155
+ /** Whether a sys_member row was inserted binding the admin to the default org. */
156
+ memberCreated: boolean;
157
+ /** Human-readable reason when the helper short-circuited. */
158
+ reason?: 'no_admin' | 'admin_already_in_org' | 'org_insert_failed' | 'member_insert_failed';
159
+ }
160
+ /**
161
+ * Ensure the platform admin has a Default Organization to operate in.
162
+ * Safe to call multiple times — idempotent on stable slug `default`
163
+ * and on the presence of any existing `sys_member` row for the admin.
164
+ */
165
+ declare function ensureDefaultOrganization(ql: any, options?: EnsureOptions): Promise<EnsureDefaultOrganizationResult>;
166
+
167
+ /**
168
+ * Canonical plugin-org-scoping manifest source.
169
+ *
170
+ * Imported by `objectstack.config.ts` (compile-time) and
171
+ * `org-scoping-plugin.ts` (runtime `manifest.register`) so the two
172
+ * registration paths cannot drift.
173
+ */
174
+ declare const ORG_SCOPING_PLUGIN_ID = "com.objectstack.plugin-org-scoping";
175
+ declare const ORG_SCOPING_PLUGIN_VERSION = "1.0.0";
176
+ /** This plugin owns no `sys_*` objects — Organization itself lives in `@objectstack/platform-objects`. */
177
+ declare const orgScopingObjects: readonly [];
178
+ /** Manifest header shared by compile-time config and runtime registration. */
179
+ declare const orgScopingPluginManifestHeader: {
180
+ id: string;
181
+ namespace: string;
182
+ version: string;
183
+ type: "plugin";
184
+ scope: "system";
185
+ defaultDatasource: string;
186
+ name: string;
187
+ description: string;
188
+ };
189
+
190
+ export { type EnsureDefaultOrganizationResult, ORG_SCOPING_PLUGIN_ID, ORG_SCOPING_PLUGIN_VERSION, OrgScopingPlugin, type OrgScopingPluginOptions, claimOrphanOrgRows, cloneOrgSeedData, ensureDefaultOrganization, orgScopingObjects, orgScopingPluginManifestHeader };