@abloatai/ablo 0.12.0 → 0.14.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/AGENTS.md +2 -2
- package/CHANGELOG.md +29 -0
- package/README.md +3 -3
- package/dist/BaseSyncedStore.js +39 -32
- package/dist/batching/index.d.ts +57 -0
- package/dist/batching/index.js +150 -0
- package/dist/cli.cjs +158 -40
- package/dist/client/Ablo.d.ts +16 -25
- package/dist/client/Ablo.js +1 -1
- package/dist/client/auth.js +11 -0
- package/dist/client/createModelProxy.d.ts +33 -8
- package/dist/client/createModelProxy.js +4 -4
- package/dist/errorCodes.d.ts +3 -1
- package/dist/errorCodes.js +10 -1
- package/dist/schema/index.d.ts +2 -2
- package/dist/schema/index.js +2 -2
- package/dist/schema/model.d.ts +38 -84
- package/dist/schema/model.js +12 -12
- package/dist/schema/roles.d.ts +49 -0
- package/dist/schema/roles.js +21 -0
- package/dist/schema/schema.d.ts +1 -1
- package/dist/schema/schema.js +1 -1
- package/dist/schema/serialize.d.ts +4 -2
- package/dist/schema/serialize.js +4 -2
- package/dist/schema/sugar.d.ts +7 -28
- package/dist/schema/sugar.js +2 -7
- package/dist/schema/sync-delta-row.d.ts +2 -0
- package/dist/schema/sync-delta-row.js +2 -1
- package/dist/schema/tenancy.d.ts +67 -28
- package/dist/schema/tenancy.js +93 -23
- package/dist/server/commit.d.ts +8 -3
- package/docs/api.md +7 -6
- package/docs/cli.md +43 -4
- package/docs/client-behavior.md +2 -2
- package/docs/coordination.md +12 -12
- package/docs/examples/agent-human.md +6 -6
- package/docs/examples/ai-sdk-tool.md +1 -1
- package/docs/examples/existing-python-backend.md +0 -2
- package/docs/examples/nextjs.md +2 -2
- package/docs/examples/scoped-agent.md +3 -3
- package/docs/examples/server-agent.md +4 -4
- package/docs/identity.md +27 -20
- package/docs/index.md +0 -1
- package/docs/integration-guide.md +12 -9
- package/docs/interaction-model.md +1 -1
- package/docs/mcp.md +17 -5
- package/docs/quickstart.md +3 -3
- package/docs/react.md +69 -0
- package/llms.txt +2 -3
- package/package.json +8 -2
- package/docs/mcp/claude-code.md +0 -35
- package/docs/mcp/cursor.md +0 -35
- package/docs/mcp/windsurf.md +0 -33
- package/docs/roadmap.md +0 -55
- package/docs/the-loop.md +0 -21
- package/llms-full.txt +0 -396
package/dist/cli.cjs
CHANGED
|
@@ -276903,9 +276903,18 @@ var ERROR_CODES = {
|
|
|
276903
276903
|
// ── quota / rate limit (429) ──────────────────────────────────────
|
|
276904
276904
|
quota_exceeded: wire("rate_limit", 429, true, "The organization exceeded its configured usage quota."),
|
|
276905
276905
|
connection_limit_exceeded: wire("rate_limit", 429, true, "Too many concurrent WebSocket connections for this principal or organization. Close idle connections, or retry once others drain."),
|
|
276906
|
+
// Per-CREDENTIAL request-rate limit — the fast (RPS/burst) axis, distinct from
|
|
276907
|
+
// the slow-axis `quota_exceeded` (org daily/monthly usage). Keyed per API key,
|
|
276908
|
+
// so one noisy key backs off without affecting the rest of the org. The
|
|
276909
|
+
// `Retry-After` header carries the bucket-refill delay.
|
|
276910
|
+
rate_limit_exceeded: wire("rate_limit", 429, true, "This API key is sending requests too quickly; slow down and retry after the indicated delay."),
|
|
276906
276911
|
// ── server (5xx) ───────────────────────────────────────────────────
|
|
276907
276912
|
internal_error: wire("server", 500, true, "An unexpected server error occurred."),
|
|
276908
276913
|
quota_lookup_failed: wire("server", 503, true, "The quota decision could not be loaded."),
|
|
276914
|
+
// The per-key rate-limiter backend (Redis) was unreachable and the API is
|
|
276915
|
+
// configured to FAIL CLOSED on that path, so the request was rejected rather
|
|
276916
|
+
// than admitted unchecked. Retryable: the next attempt re-probes the backend.
|
|
276917
|
+
rate_limiter_unavailable: wire("server", 503, true, "The rate-limiter backend is unavailable and this endpoint is configured to fail closed; retry shortly."),
|
|
276909
276918
|
turn_open_failed: wire("server", 500, true, "The agent turn failed to open."),
|
|
276910
276919
|
turn_close_failed: wire("server", 500, true, "The agent turn failed to close cleanly."),
|
|
276911
276920
|
// ── client-only invariants (never serialized) ──────────────────────
|
|
@@ -277023,6 +277032,7 @@ var roleSchema = import_zod2.z.object({
|
|
|
277023
277032
|
kind: import_zod2.z.string().regex(/^[a-z][a-z0-9_]*$/, 'kind must be a lowercase identifier, e.g. "deck"'),
|
|
277024
277033
|
source: roleSourceSchema
|
|
277025
277034
|
});
|
|
277035
|
+
var entityRoleSchema = roleSchema;
|
|
277026
277036
|
var scopeSchema = import_zod2.z.union([
|
|
277027
277037
|
import_zod2.z.boolean(),
|
|
277028
277038
|
import_zod2.z.string().regex(/^[a-z][a-z0-9_]*$/, 'scope kind must be a lowercase identifier, e.g. "dataroom"')
|
|
@@ -277031,6 +277041,11 @@ var grantsRefSchema = import_zod2.z.object({
|
|
|
277031
277041
|
subject: import_zod2.z.string().regex(/^[a-zA-Z_][a-zA-Z0-9_]*$/, "grants.subject must name a relation"),
|
|
277032
277042
|
scope: import_zod2.z.string().regex(/^[a-zA-Z_][a-zA-Z0-9_]*$/, "grants.scope must name a relation")
|
|
277033
277043
|
});
|
|
277044
|
+
var groupsInputSchema = import_zod2.z.object({
|
|
277045
|
+
root: scopeSchema.optional(),
|
|
277046
|
+
grants: grantsRefSchema.optional(),
|
|
277047
|
+
roles: import_zod2.z.union([entityRoleSchema, import_zod2.z.array(entityRoleSchema)]).optional()
|
|
277048
|
+
});
|
|
277034
277049
|
|
|
277035
277050
|
// src/coordination/schema.ts
|
|
277036
277051
|
var targetRangeSchema = import_zod3.z.object({
|
|
@@ -279646,6 +279661,7 @@ init_cjs_shims();
|
|
|
279646
279661
|
var import_os2 = require("os");
|
|
279647
279662
|
var import_path2 = require("path");
|
|
279648
279663
|
var import_fs3 = require("fs");
|
|
279664
|
+
var DEFAULT_PROFILE = "default";
|
|
279649
279665
|
function configDir() {
|
|
279650
279666
|
if (process.env.ABLO_CONFIG_DIR) return process.env.ABLO_CONFIG_DIR;
|
|
279651
279667
|
const xdg = process.env.XDG_CONFIG_HOME;
|
|
@@ -279657,12 +279673,32 @@ function configPath() {
|
|
|
279657
279673
|
function credentialsPath() {
|
|
279658
279674
|
return (0, import_path2.join)(configDir(), "credentials.json");
|
|
279659
279675
|
}
|
|
279676
|
+
function activeProfileName(cfg) {
|
|
279677
|
+
return cfg.activeProject?.slug ?? DEFAULT_PROFILE;
|
|
279678
|
+
}
|
|
279660
279679
|
function asKeyEntry(value) {
|
|
279661
279680
|
if (value && typeof value === "object" && typeof value.apiKey === "string") {
|
|
279662
279681
|
return value;
|
|
279663
279682
|
}
|
|
279664
279683
|
return void 0;
|
|
279665
279684
|
}
|
|
279685
|
+
function asProfileKeys(value) {
|
|
279686
|
+
if (!value || typeof value !== "object") return void 0;
|
|
279687
|
+
const v = value;
|
|
279688
|
+
const sandbox = asKeyEntry(v.sandbox);
|
|
279689
|
+
const production = asKeyEntry(v.production);
|
|
279690
|
+
if (!sandbox && !production) return void 0;
|
|
279691
|
+
return { ...sandbox ? { sandbox } : {}, ...production ? { production } : {} };
|
|
279692
|
+
}
|
|
279693
|
+
function asProfileMap(value) {
|
|
279694
|
+
if (!value || typeof value !== "object") return {};
|
|
279695
|
+
const out = {};
|
|
279696
|
+
for (const [name, v] of Object.entries(value)) {
|
|
279697
|
+
const keys = asProfileKeys(v);
|
|
279698
|
+
if (keys) out[name] = keys;
|
|
279699
|
+
}
|
|
279700
|
+
return out;
|
|
279701
|
+
}
|
|
279666
279702
|
function readJson(path) {
|
|
279667
279703
|
if (!(0, import_fs3.existsSync)(path)) return null;
|
|
279668
279704
|
try {
|
|
@@ -279683,7 +279719,7 @@ function normalizeStoredMode(value) {
|
|
|
279683
279719
|
if (value === "sandbox" || value === "production") return value;
|
|
279684
279720
|
return void 0;
|
|
279685
279721
|
}
|
|
279686
|
-
function
|
|
279722
|
+
function extractLegacyEntries(obj) {
|
|
279687
279723
|
const sandbox = asKeyEntry(obj.sandbox);
|
|
279688
279724
|
const production = asKeyEntry(obj.production);
|
|
279689
279725
|
if (sandbox || production) {
|
|
@@ -279692,24 +279728,33 @@ function extractEntries(obj) {
|
|
|
279692
279728
|
const flat = asKeyEntry(obj);
|
|
279693
279729
|
return flat ? { sandbox: flat } : {};
|
|
279694
279730
|
}
|
|
279731
|
+
function hasKey(keys) {
|
|
279732
|
+
return !!(keys?.sandbox || keys?.production);
|
|
279733
|
+
}
|
|
279695
279734
|
function readConfig() {
|
|
279696
279735
|
const cfgObj = readJson(configPath());
|
|
279697
279736
|
const credObj = readJson(credentialsPath());
|
|
279698
279737
|
const mode2 = normalizeStoredMode(cfgObj?.mode) ?? normalizeStoredMode(credObj?.mode);
|
|
279699
279738
|
const activeProject = asActiveProject(cfgObj?.activeProject);
|
|
279700
|
-
const
|
|
279701
|
-
const
|
|
279702
|
-
...
|
|
279703
|
-
...
|
|
279704
|
-
// credentials file wins
|
|
279739
|
+
const activeName = activeProject?.slug ?? DEFAULT_PROFILE;
|
|
279740
|
+
const profiles = {
|
|
279741
|
+
...asProfileMap(credObj?.profiles),
|
|
279742
|
+
...asProfileMap(cfgObj?.profiles)
|
|
279705
279743
|
};
|
|
279706
|
-
|
|
279744
|
+
const legacyCfg = cfgObj ? extractLegacyEntries(cfgObj) : {};
|
|
279745
|
+
const legacyCred = credObj ? extractLegacyEntries(credObj) : {};
|
|
279746
|
+
const legacy = { ...legacyCfg, ...legacyCred };
|
|
279747
|
+
const migratedLegacy = hasKey(legacy) && !hasKey(profiles[activeName]);
|
|
279748
|
+
if (migratedLegacy) profiles[activeName] = legacy;
|
|
279749
|
+
const anyKey = Object.values(profiles).some(hasKey);
|
|
279750
|
+
if (!mode2 && !anyKey) return null;
|
|
279707
279751
|
const config = {
|
|
279708
279752
|
mode: mode2 ?? "sandbox",
|
|
279709
279753
|
...activeProject ? { activeProject } : {},
|
|
279710
|
-
|
|
279754
|
+
profiles
|
|
279711
279755
|
};
|
|
279712
|
-
|
|
279756
|
+
const secretsInConfig = hasKey(legacyCfg);
|
|
279757
|
+
if (secretsInConfig || migratedLegacy) writeConfig(config);
|
|
279713
279758
|
return config;
|
|
279714
279759
|
}
|
|
279715
279760
|
function writeConfig(cfg) {
|
|
@@ -279725,16 +279770,34 @@ function writeConfig(cfg) {
|
|
|
279725
279770
|
`,
|
|
279726
279771
|
{ mode: 384 }
|
|
279727
279772
|
);
|
|
279728
|
-
const
|
|
279729
|
-
|
|
279730
|
-
|
|
279731
|
-
|
|
279732
|
-
|
|
279773
|
+
const profiles = {};
|
|
279774
|
+
for (const [name, keys] of Object.entries(cfg.profiles)) {
|
|
279775
|
+
if (!hasKey(keys)) continue;
|
|
279776
|
+
profiles[name] = {
|
|
279777
|
+
...keys.sandbox ? { sandbox: keys.sandbox } : {},
|
|
279778
|
+
...keys.production ? { production: keys.production } : {}
|
|
279779
|
+
};
|
|
279780
|
+
}
|
|
279781
|
+
(0, import_fs3.writeFileSync)(credentialsPath(), `${JSON.stringify({ profiles }, null, 2)}
|
|
279733
279782
|
`, { mode: 384 });
|
|
279734
279783
|
return credentialsPath();
|
|
279735
279784
|
}
|
|
279785
|
+
function emptyConfig(mode2 = "sandbox") {
|
|
279786
|
+
return { mode: mode2, profiles: {} };
|
|
279787
|
+
}
|
|
279788
|
+
function setProfileKeys(profileName, keys, opts) {
|
|
279789
|
+
const cfg = readConfig() ?? emptyConfig(opts.mode);
|
|
279790
|
+
cfg.mode = opts.mode;
|
|
279791
|
+
cfg.profiles[profileName] = {
|
|
279792
|
+
...keys.sandbox ? { sandbox: keys.sandbox } : {},
|
|
279793
|
+
...keys.production ? { production: keys.production } : {}
|
|
279794
|
+
};
|
|
279795
|
+
if (opts.activeProject) cfg.activeProject = opts.activeProject;
|
|
279796
|
+
else delete cfg.activeProject;
|
|
279797
|
+
return writeConfig(cfg);
|
|
279798
|
+
}
|
|
279736
279799
|
function setMode(mode2) {
|
|
279737
|
-
const cfg = readConfig() ??
|
|
279800
|
+
const cfg = readConfig() ?? emptyConfig(mode2);
|
|
279738
279801
|
cfg.mode = mode2;
|
|
279739
279802
|
return writeConfig(cfg);
|
|
279740
279803
|
}
|
|
@@ -279745,13 +279808,15 @@ function getActiveProject() {
|
|
|
279745
279808
|
return readConfig()?.activeProject;
|
|
279746
279809
|
}
|
|
279747
279810
|
function setActiveProject(project) {
|
|
279748
|
-
const cfg = readConfig() ??
|
|
279811
|
+
const cfg = readConfig() ?? emptyConfig("sandbox");
|
|
279749
279812
|
if (project) cfg.activeProject = project;
|
|
279750
279813
|
else delete cfg.activeProject;
|
|
279751
279814
|
return writeConfig(cfg);
|
|
279752
279815
|
}
|
|
279753
279816
|
function getKeyEntry(mode2) {
|
|
279754
|
-
|
|
279817
|
+
const cfg = readConfig();
|
|
279818
|
+
if (!cfg) return void 0;
|
|
279819
|
+
return cfg.profiles[activeProfileName(cfg)]?.[mode2];
|
|
279755
279820
|
}
|
|
279756
279821
|
function modeFromKey(key) {
|
|
279757
279822
|
if (/^(sk|rk)_test_/.test(key)) return "sandbox";
|
|
@@ -279775,11 +279840,21 @@ function resolveApiKey(modeOverride) {
|
|
|
279775
279840
|
if (process.env.ABLO_API_KEY) return process.env.ABLO_API_KEY;
|
|
279776
279841
|
const cfg = readConfig();
|
|
279777
279842
|
if (!cfg) return void 0;
|
|
279778
|
-
const entry = cfg[modeOverride ?? cfg.mode];
|
|
279843
|
+
const entry = cfg.profiles[activeProfileName(cfg)]?.[modeOverride ?? cfg.mode];
|
|
279779
279844
|
if (!entry) return void 0;
|
|
279780
279845
|
if (entry.expiresAt && Date.parse(entry.expiresAt) <= Date.now()) return void 0;
|
|
279781
279846
|
return entry.apiKey;
|
|
279782
279847
|
}
|
|
279848
|
+
function guardActiveProjectKey() {
|
|
279849
|
+
if (process.env.ABLO_API_KEY) {
|
|
279850
|
+
return { ok: true, activeProfile: DEFAULT_PROFILE, available: [] };
|
|
279851
|
+
}
|
|
279852
|
+
const cfg = readConfig();
|
|
279853
|
+
const activeProfile = cfg ? activeProfileName(cfg) : DEFAULT_PROFILE;
|
|
279854
|
+
const profiles = cfg?.profiles ?? {};
|
|
279855
|
+
const available = Object.entries(profiles).filter(([, keys]) => hasKey(keys)).map(([name]) => name);
|
|
279856
|
+
return { ok: hasKey(profiles[activeProfile]), activeProfile, available };
|
|
279857
|
+
}
|
|
279783
279858
|
function resolvePushPlan() {
|
|
279784
279859
|
const envKey = process.env.ABLO_API_KEY;
|
|
279785
279860
|
if (envKey) return { flow: modeFromKey(envKey) ?? getMode(), apiKey: envKey, source: "env" };
|
|
@@ -280425,8 +280500,19 @@ function openBrowser(url) {
|
|
|
280425
280500
|
} catch {
|
|
280426
280501
|
}
|
|
280427
280502
|
}
|
|
280428
|
-
|
|
280503
|
+
function parseProjectFlag(argv) {
|
|
280504
|
+
const i = argv.indexOf("--project");
|
|
280505
|
+
if (i >= 0) {
|
|
280506
|
+
const slug = argv[i + 1];
|
|
280507
|
+
if (slug && !slug.startsWith("-")) return slug;
|
|
280508
|
+
}
|
|
280509
|
+
const eq = argv.find((a) => a.startsWith("--project="));
|
|
280510
|
+
return eq ? eq.slice("--project=".length) || void 0 : void 0;
|
|
280511
|
+
}
|
|
280512
|
+
async function deviceLogin(argv) {
|
|
280429
280513
|
Ie(`${brand("ablo")} login`);
|
|
280514
|
+
const requested = parseProjectFlag(argv) ?? getActiveProject()?.slug;
|
|
280515
|
+
const targetProject = requested === DEFAULT_PROFILE ? void 0 : requested;
|
|
280430
280516
|
const interactive = Boolean(process.stdout.isTTY && process.stdin.isTTY);
|
|
280431
280517
|
let account = "login";
|
|
280432
280518
|
if (interactive) {
|
|
@@ -280504,13 +280590,19 @@ ${import_picocolors7.default.dim(url)}`, "Approve in your browser");
|
|
|
280504
280590
|
s.stop("Timed out waiting for approval.");
|
|
280505
280591
|
process.exit(1);
|
|
280506
280592
|
}
|
|
280507
|
-
s.message(
|
|
280593
|
+
s.message(
|
|
280594
|
+
targetProject ? `Provisioning keys for ${targetProject}\u2026` : "Provisioning a sandbox key\u2026"
|
|
280595
|
+
);
|
|
280508
280596
|
const provRes = await fetch(`${AUTH_URL}/api/cli/provision-key`, {
|
|
280509
280597
|
method: "POST",
|
|
280510
280598
|
headers: { authorization: `Bearer ${accessToken}`, "content-type": "application/json" },
|
|
280511
|
-
//
|
|
280512
|
-
//
|
|
280513
|
-
|
|
280599
|
+
// Scope the minted keys to the chosen project (`--project`/active), with
|
|
280600
|
+
// the device_code as a legacy fallback for the /cli picker. Both harmless
|
|
280601
|
+
// if absent → org-default keys.
|
|
280602
|
+
body: JSON.stringify({
|
|
280603
|
+
device_code: code.device_code,
|
|
280604
|
+
...targetProject ? { project_slug: targetProject } : {}
|
|
280605
|
+
})
|
|
280514
280606
|
}).catch(() => null);
|
|
280515
280607
|
if (!provRes || !provRes.ok) {
|
|
280516
280608
|
s.stop("Could not provision a key.");
|
|
@@ -280528,16 +280620,23 @@ ${import_picocolors7.default.dim(url)}`, "Approve in your browser");
|
|
|
280528
280620
|
...prov.organizationId ? { organizationId: prov.organizationId } : {},
|
|
280529
280621
|
...k3.expiresAt ? { expiresAt: k3.expiresAt } : {}
|
|
280530
280622
|
});
|
|
280531
|
-
const
|
|
280532
|
-
|
|
280533
|
-
|
|
280534
|
-
|
|
280535
|
-
|
|
280623
|
+
const profileName = prov.project?.slug ?? DEFAULT_PROFILE;
|
|
280624
|
+
const path = setProfileKeys(
|
|
280625
|
+
profileName,
|
|
280626
|
+
{
|
|
280627
|
+
sandbox: entry(prov.test),
|
|
280628
|
+
...prov.live ? { production: entry(prov.live) } : {}
|
|
280629
|
+
},
|
|
280630
|
+
{ mode: "sandbox", activeProject: prov.project ?? void 0 }
|
|
280631
|
+
);
|
|
280536
280632
|
s.stop(`Saved keys to ${path}`);
|
|
280537
|
-
|
|
280633
|
+
const where = prov.project ? ` ${import_picocolors7.default.dim(`(project ${prov.project.slug})`)}` : "";
|
|
280634
|
+
Se(
|
|
280635
|
+
`${import_picocolors7.default.green("\u2713")} Logged in ${import_picocolors7.default.dim("(sandbox)")}${where}. Run ${import_picocolors7.default.bold("npx ablo push")} to push your schema.`
|
|
280636
|
+
);
|
|
280538
280637
|
}
|
|
280539
|
-
async function login() {
|
|
280540
|
-
await deviceLogin();
|
|
280638
|
+
async function login(argv = []) {
|
|
280639
|
+
await deviceLogin(argv);
|
|
280541
280640
|
}
|
|
280542
280641
|
function logout() {
|
|
280543
280642
|
const removed = clearCredential();
|
|
@@ -280790,11 +280889,13 @@ async function projects(argv) {
|
|
|
280790
280889
|
setActiveProject({ id: target.id, slug: target.slug });
|
|
280791
280890
|
console.log(` ${import_picocolors9.default.green("\u2713")} now targeting project ${import_picocolors9.default.bold(target.slug)} ${import_picocolors9.default.dim(`(${target.id})`)}`);
|
|
280792
280891
|
}
|
|
280793
|
-
|
|
280794
|
-
|
|
280795
|
-
|
|
280796
|
-
|
|
280797
|
-
|
|
280892
|
+
const guard = guardActiveProjectKey();
|
|
280893
|
+
if (!guard.ok) {
|
|
280894
|
+
const loginCmd = guard.activeProfile === DEFAULT_PROFILE ? "ablo login" : `ablo login --project ${guard.activeProfile}`;
|
|
280895
|
+
console.log(
|
|
280896
|
+
import_picocolors9.default.dim(` No key stored for this project yet \u2014 run ${import_picocolors9.default.bold(loginCmd)} to mint one.`)
|
|
280897
|
+
);
|
|
280898
|
+
}
|
|
280798
280899
|
return;
|
|
280799
280900
|
}
|
|
280800
280901
|
console.error(
|
|
@@ -281370,7 +281471,7 @@ function spreadOpts(optsArg) {
|
|
|
281370
281471
|
}
|
|
281371
281472
|
return `, ...${optsArg.getText()}`;
|
|
281372
281473
|
}
|
|
281373
|
-
function
|
|
281474
|
+
function hasKey2(obj, key) {
|
|
281374
281475
|
return obj.getProperties().some(
|
|
281375
281476
|
(p2) => (import_ts_morph.Node.isPropertyAssignment(p2) || import_ts_morph.Node.isShorthandPropertyAssignment(p2)) && p2.getName() === key
|
|
281376
281477
|
);
|
|
@@ -281383,7 +281484,7 @@ function verbRewrite(call, verb) {
|
|
|
281383
281484
|
const first = args[0];
|
|
281384
281485
|
const calleeText = call.getExpression().getText();
|
|
281385
281486
|
if (verb === "create") {
|
|
281386
|
-
if (import_ts_morph.Node.isObjectLiteralExpression(first) &&
|
|
281487
|
+
if (import_ts_morph.Node.isObjectLiteralExpression(first) && hasKey2(first, "data")) return null;
|
|
281387
281488
|
return `${calleeText}({ data: ${first.getText()}${spreadOpts(args[1])} })`;
|
|
281388
281489
|
}
|
|
281389
281490
|
if (import_ts_morph.Node.isObjectLiteralExpression(first)) return null;
|
|
@@ -282212,7 +282313,7 @@ async function main() {
|
|
|
282212
282313
|
if (command === "init") {
|
|
282213
282314
|
await init(process.argv.slice(3));
|
|
282214
282315
|
} else if (command === "login") {
|
|
282215
|
-
await login();
|
|
282316
|
+
await login(process.argv.slice(3));
|
|
282216
282317
|
} else if (command === "logout") {
|
|
282217
282318
|
logout();
|
|
282218
282319
|
} else if (command === "mode") {
|
|
@@ -282251,6 +282352,22 @@ async function main() {
|
|
|
282251
282352
|
const rest = process.argv.slice(3);
|
|
282252
282353
|
const advanced = rest.some((a) => ["--force", "--rename", "--backfill", "--url"].includes(a));
|
|
282253
282354
|
const watching = rest.includes("--watch");
|
|
282355
|
+
const guard = guardActiveProjectKey();
|
|
282356
|
+
if (!guard.ok && guard.available.length > 0 && !rest.includes("--url")) {
|
|
282357
|
+
console.error(
|
|
282358
|
+
` ${import_picocolors18.default.yellow("\u26A0")} active project ${import_picocolors18.default.bold(guard.activeProfile)} has no stored key ${import_picocolors18.default.dim(
|
|
282359
|
+
`(you have keys for: ${guard.available.join(", ")})`
|
|
282360
|
+
)}`
|
|
282361
|
+
);
|
|
282362
|
+
const loginCmd = guard.activeProfile === "default" ? "ablo login" : `ablo login --project ${guard.activeProfile}`;
|
|
282363
|
+
console.error(
|
|
282364
|
+
import_picocolors18.default.dim(
|
|
282365
|
+
` Mint one with ${import_picocolors18.default.bold(loginCmd)}, or switch with ${import_picocolors18.default.bold("ablo projects use <slug>")}.`
|
|
282366
|
+
)
|
|
282367
|
+
);
|
|
282368
|
+
process.exitCode = 1;
|
|
282369
|
+
return;
|
|
282370
|
+
}
|
|
282254
282371
|
const plan = resolvePushPlan();
|
|
282255
282372
|
if (advanced || plan.flow === "production" && !watching) {
|
|
282256
282373
|
await push(rest);
|
|
@@ -282275,11 +282392,12 @@ async function main() {
|
|
|
282275
282392
|
console.log(` [--auth apikey] [--storage direct|endpoint] [--project <slug>] [--no-project]`);
|
|
282276
282393
|
console.log(` [--no-agent] [--no-pull] [--no-install] [--no-login]`);
|
|
282277
282394
|
console.log(` npx ablo login Authorize in your browser (provisions sandbox + production keys)`);
|
|
282395
|
+
console.log(` npx ablo login --project <slug> Same, scoped to a project (mints its keys, makes it active)`);
|
|
282278
282396
|
console.log(` npx ablo logout Remove the stored API key`);
|
|
282279
282397
|
console.log(` npx ablo mode [sandbox|production] Switch active environment, like Stripe`);
|
|
282280
282398
|
console.log(` npx ablo projects list List the org's projects (default + your own)`);
|
|
282281
282399
|
console.log(` npx ablo projects create <slug> Create a project (its keys/schema/data are isolated)`);
|
|
282282
|
-
console.log(` npx ablo projects use <slug|default>
|
|
282400
|
+
console.log(` npx ablo projects use <slug|default> Switch the active project (run login --project to mint its keys)`);
|
|
282283
282401
|
console.log(` npx ablo status Show org, mode, keys, and server health`);
|
|
282284
282402
|
console.log(` npx ablo status --json Same, machine-readable (mode, key prefix, org id, api host)`);
|
|
282285
282403
|
console.log(` npx ablo logs [-n N] [--since 15m] Tail commit activity (follows; --no-follow to exit)`);
|
package/dist/client/Ablo.d.ts
CHANGED
|
@@ -28,7 +28,7 @@ import type { SyncWebSocket } from '../sync/SyncWebSocket.js';
|
|
|
28
28
|
import type { SyncGroupInput } from '../schema/roles.js';
|
|
29
29
|
import { type SyncStatus } from '../BaseSyncedStore.js';
|
|
30
30
|
import type { ClaimStream, ClaimWaitOptions, PresenceStream, Snapshot } from '../types/streams.js';
|
|
31
|
-
import type { ClaimHandle, Duration
|
|
31
|
+
import type { ClaimHandle, Duration } from '../types/streams.js';
|
|
32
32
|
import { type AbloApi, type AbloApiClientOptions, type AbloApiClaims } from './ApiClient.js';
|
|
33
33
|
import { type AbloHttpClient, type AbloHttpClientOptions } from './httpClient.js';
|
|
34
34
|
/**
|
|
@@ -371,7 +371,7 @@ export interface InternalAbloOptions<S extends SchemaRecord = SchemaRecord> {
|
|
|
371
371
|
* `claim({ id })` — durable claim handle for coordinated writes
|
|
372
372
|
*/
|
|
373
373
|
export type { LocalCountOptions, LocalReadOptions, ModelListScope, ServerReadOptions, ModelRetrieveParams, ModelCreateParams, ModelUpdateParams, ModelDeleteParams, ClaimOptions, ClaimParams, ClaimLookupParams, ClaimReorderParams, ClaimHandle, ModelOperations, } from './createModelProxy.js';
|
|
374
|
-
import type { ModelOperations, ClaimOptions, ClaimParams,
|
|
374
|
+
import type { ModelOperations, ClaimOptions, ClaimParams, ClaimReadApi, AwaitedClaimMethod, ServerReadOptions } from './createModelProxy.js';
|
|
375
375
|
export type ModelOperationAction = 'create' | 'update' | 'delete' | 'archive' | 'unarchive';
|
|
376
376
|
export type CommitWait = 'queued' | 'confirmed';
|
|
377
377
|
export interface ModelRead<T = Record<string, unknown>> {
|
|
@@ -473,30 +473,21 @@ export interface ModelMutationOptions extends ClaimedOptions {
|
|
|
473
473
|
* The HTTP/stateless claim surface. Normal tools usually put `claim` directly
|
|
474
474
|
* on the write (`update({ id, data, claim })`) and let the SDK release it. Use
|
|
475
475
|
* this namespace for multi-step handles and coordination screens.
|
|
476
|
+
*
|
|
477
|
+
* Same surface as the reactive {@link ClaimApi}, but every read is a server
|
|
478
|
+
* round-trip, so `state`/`queue`/`reorder` are **awaited** here (the WebSocket
|
|
479
|
+
* client resolves them synchronously from its local pool — which is what lets
|
|
480
|
+
* `useAblo((ablo) => ablo.x.claim.state({ id }))` work inside a React render; a
|
|
481
|
+
* stateless client has no pool to read, so the `Promise` is unavoidable).
|
|
482
|
+
*
|
|
483
|
+
* Mechanically DERIVED from `ClaimReadApi` via {@link AwaitedClaimMethod} so the
|
|
484
|
+
* two transports can never drift: the ONLY difference is the uniform `Promise`
|
|
485
|
+
* wrapper that statelessness forces. `claim({ id })` is identical (already async
|
|
486
|
+
* on both); `state`/`queue`/`reorder`/`release` are the awaited form.
|
|
476
487
|
*/
|
|
477
|
-
export
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
/** Release a manual claim you hold. */
|
|
481
|
-
release(params: ClaimLookupParams<T> | ClaimHandle<T>): Promise<void>;
|
|
482
|
-
/**
|
|
483
|
-
* Current holder of the lease on a row, or `null` when free. For UI badges,
|
|
484
|
-
* preflight checks, and operators.
|
|
485
|
-
*/
|
|
486
|
-
state(params: ClaimLookupParams<T>): Promise<Claim | null>;
|
|
487
|
-
/**
|
|
488
|
-
* FIFO wait line behind the holder. Advanced: useful for operator UIs and
|
|
489
|
-
* schedulers.
|
|
490
|
-
*/
|
|
491
|
-
queue(params: ClaimLookupParams<T>): Promise<{
|
|
492
|
-
readonly object: 'list';
|
|
493
|
-
readonly data: readonly Claim[];
|
|
494
|
-
}>;
|
|
495
|
-
/**
|
|
496
|
-
* Re-rank the wait line. Advanced and permission-gated.
|
|
497
|
-
*/
|
|
498
|
-
reorder(params: ClaimReorderParams<T>): Promise<void>;
|
|
499
|
-
}
|
|
488
|
+
export type HttpClaimApi<T = Record<string, unknown>> = ((params: ClaimParams<T>) => Promise<ClaimHandle<T>>) & {
|
|
489
|
+
[K in keyof ClaimReadApi<T>]: AwaitedClaimMethod<ClaimReadApi<T>[K]>;
|
|
490
|
+
};
|
|
500
491
|
export interface ModelClient<T = Record<string, unknown>> {
|
|
501
492
|
/**
|
|
502
493
|
* Single-row read over HTTP. **Returns an envelope, not the bare row** — the
|
package/dist/client/Ablo.js
CHANGED
|
@@ -1325,7 +1325,7 @@ export function Ablo(options) {
|
|
|
1325
1325
|
}),
|
|
1326
1326
|
queue: (target) => publicClaims.queueFor({ type: target.model, id: target.id }),
|
|
1327
1327
|
reorder: (target, order) => publicClaims.reorder({ type: target.model, id: target.id }, order),
|
|
1328
|
-
|
|
1328
|
+
state: (target) => {
|
|
1329
1329
|
// The live claim stream only tracks *open* (active) claims;
|
|
1330
1330
|
// terminal states (committed / expired / canceled) drop out of
|
|
1331
1331
|
// the list entirely — exactly the ephemeral coordination model.
|
package/dist/client/auth.js
CHANGED
|
@@ -54,6 +54,14 @@ export function resolveDatabaseUrl(input) {
|
|
|
54
54
|
* explicit option instead of flipping their mode for them. Warns once per process
|
|
55
55
|
* so it never spams, and falls back to `console.warn` when no logger is supplied
|
|
56
56
|
* (the `transport: 'api'` client has none).
|
|
57
|
+
*
|
|
58
|
+
* Suppressed entirely on the hosted/token path: if an `apiKey` resolves (option
|
|
59
|
+
* or `ABLO_API_KEY` env), the caller has chosen the hosted capability-token /
|
|
60
|
+
* Data Source transport, which is mutually exclusive with direct `databaseUrl`
|
|
61
|
+
* mode. A `DATABASE_URL` sitting in that environment is unrelated infra (Prisma,
|
|
62
|
+
* Drizzle, the sync-server) — never an omitted option — so nudging would be a
|
|
63
|
+
* false positive. This is the first-party hosted app's exact shape, where the
|
|
64
|
+
* stray nudge otherwise reaches end-user desktop logs.
|
|
57
65
|
*/
|
|
58
66
|
let warnedDatabaseUrlEnvIgnored = false;
|
|
59
67
|
export function warnIfDatabaseUrlEnvIgnored(input, warn) {
|
|
@@ -61,6 +69,9 @@ export function warnIfDatabaseUrlEnvIgnored(input, warn) {
|
|
|
61
69
|
return;
|
|
62
70
|
if (input.options.databaseUrl != null)
|
|
63
71
|
return;
|
|
72
|
+
// Hosted/token path → DATABASE_URL is unrelated infra, not an omitted option.
|
|
73
|
+
if (resolveApiKey(input) != null)
|
|
74
|
+
return;
|
|
64
75
|
const envUrl = input.env.DATABASE_URL;
|
|
65
76
|
if (typeof envUrl !== 'string' || envUrl.length === 0)
|
|
66
77
|
return;
|
|
@@ -109,8 +109,13 @@ export interface ModelCollaboration<T> {
|
|
|
109
109
|
* `null` when the target is free. The wiring site computes it because
|
|
110
110
|
* only it knows the local participant id (needed to distinguish "I
|
|
111
111
|
* hold it" from "someone else holds it").
|
|
112
|
+
*
|
|
113
|
+
* Named `state` to match the public `ablo.<model>.claim.state({ id })` read —
|
|
114
|
+
* one verb for "who holds this" across every claim surface; the only
|
|
115
|
+
* difference is this internal contract takes an explicit `{ model, id }`
|
|
116
|
+
* target because it isn't bound to a single model.
|
|
112
117
|
*/
|
|
113
|
-
|
|
118
|
+
state(target: {
|
|
114
119
|
model: string;
|
|
115
120
|
id: string;
|
|
116
121
|
}): Claim | null;
|
|
@@ -202,10 +207,11 @@ export interface ClaimTargetOptions<T = Record<string, unknown>> {
|
|
|
202
207
|
* work-distribution dedup ("if someone else has this job, skip it") where
|
|
203
208
|
* waiting would mean double-processing.
|
|
204
209
|
*
|
|
205
|
-
* Named `queue` to match every other claim surface
|
|
206
|
-
*
|
|
207
|
-
*
|
|
208
|
-
*
|
|
210
|
+
* Named `queue` to match every other claim surface — `ablo.<model>.claim`
|
|
211
|
+
* on both the WS and HTTP clients (take-a-claim is the callable `claim({ id
|
|
212
|
+
* })` on both; the HTTP reads are just awaited) and the wire. The high-level
|
|
213
|
+
* typed claim defaults it ON because it serializes writers; the low-level
|
|
214
|
+
* lease and HTTP default it OFF — they return/resolve immediately and can't
|
|
209
215
|
* transparently wait for a grant.
|
|
210
216
|
*/
|
|
211
217
|
queue?: boolean;
|
|
@@ -276,9 +282,18 @@ export type ClaimOptions<T = Record<string, unknown>> = ClaimTargetOptions<T>;
|
|
|
276
282
|
* handle. `state`, `queue`, and `reorder` are coordination reads/scheduler
|
|
277
283
|
* controls for UI and operators.
|
|
278
284
|
*/
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
285
|
+
/**
|
|
286
|
+
* Coordination reads + scheduler controls on a claim namespace, in their
|
|
287
|
+
* REACTIVE (synchronous) form — `state`/`queue`/`reorder` resolve against the
|
|
288
|
+
* local pool with no round-trip, which is what lets `useAblo((ablo) =>
|
|
289
|
+
* ablo.x.claim.state({ id }))` read coordination state inside a React render.
|
|
290
|
+
*
|
|
291
|
+
* This is the single source of truth for the claim read surface: the stateless
|
|
292
|
+
* HTTP client exposes the *awaited* projection of EXACTLY these methods
|
|
293
|
+
* (`HttpClaimApi` in `Ablo.ts`, derived via {@link AwaitedClaimMethod}), so the
|
|
294
|
+
* two transports can never drift — edit a signature here and HTTP follows.
|
|
295
|
+
*/
|
|
296
|
+
export interface ClaimReadApi<T = Record<string, unknown>> {
|
|
282
297
|
/**
|
|
283
298
|
* Current holder for a row, or `null` when free. Use this for UI badges and
|
|
284
299
|
* preflight checks, not for the normal write path.
|
|
@@ -299,6 +314,16 @@ export interface ClaimApi<T> {
|
|
|
299
314
|
/** Release a manual claim handle early. Single-write claims auto-release. */
|
|
300
315
|
release(params: ClaimLookupParams<T> | ClaimHandle<T>): Promise<void>;
|
|
301
316
|
}
|
|
317
|
+
/**
|
|
318
|
+
* The awaited form of a claim method: a synchronous return becomes a `Promise`,
|
|
319
|
+
* an already-async one (`release`) is left untouched. Used to derive the
|
|
320
|
+
* stateless HTTP claim surface from the reactive {@link ClaimReadApi}.
|
|
321
|
+
*/
|
|
322
|
+
export type AwaitedClaimMethod<F> = F extends (...args: infer A) => infer R ? R extends Promise<unknown> ? (...args: A) => R : (...args: A) => Promise<R> : F;
|
|
323
|
+
export interface ClaimApi<T> extends ClaimReadApi<T> {
|
|
324
|
+
/** Take a claim and get an explicit held-work handle back. */
|
|
325
|
+
(params: ClaimParams<T>): Promise<ClaimHandle<T>>;
|
|
326
|
+
}
|
|
302
327
|
export interface ModelRetrieveParams extends ServerRetrieveOptions {
|
|
303
328
|
readonly id: string;
|
|
304
329
|
}
|
|
@@ -141,7 +141,7 @@ export function createModelProxy(schemaKey, registeredModelName, objectPool, syn
|
|
|
141
141
|
// Is someone ELSE already on this target? Read the local coordination
|
|
142
142
|
// snapshot up front — it decides whether we'll need to re-read after the
|
|
143
143
|
// claim (a free / already-mine target can't have changed under us).
|
|
144
|
-
const held = collaboration.
|
|
144
|
+
const held = collaboration.state({ model: wireModel, id });
|
|
145
145
|
const contended = !!held && held.heldBy !== collaboration.selfParticipantId;
|
|
146
146
|
const failFast = options?.queue === false;
|
|
147
147
|
// Fail-fast (`queue: false`): if another participant already holds it,
|
|
@@ -215,7 +215,7 @@ export function createModelProxy(schemaKey, registeredModelName, objectPool, syn
|
|
|
215
215
|
const snapshot = collaboration.createSnapshot(schemaKey, id);
|
|
216
216
|
const reason = options?.reason ?? 'editing';
|
|
217
217
|
// The self-claim's `EntityRef` mirrors what a peer's `claim.state` would
|
|
218
|
-
// report (`
|
|
218
|
+
// report (`state` maps `held.target.model` → `type`), so a holder and a
|
|
219
219
|
// peer see the SAME target.type for one row — the wire model token.
|
|
220
220
|
const selfTarget = {
|
|
221
221
|
type: wireModel,
|
|
@@ -271,7 +271,7 @@ export function createModelProxy(schemaKey, registeredModelName, objectPool, syn
|
|
|
271
271
|
// presence. Soft + fire-and-forget — never blocks or rejects the read.
|
|
272
272
|
void collaboration?.enterScope?.({ [schemaKey]: params.id });
|
|
273
273
|
// Self-awareness: the server excludes a holder's OWN presence frames and
|
|
274
|
-
// the client skips them, so `
|
|
274
|
+
// the client skips them, so `state` returns null for a row WE hold.
|
|
275
275
|
// Synthesize the active claim for self from the stored lease so the
|
|
276
276
|
// holder sees its own claim (the JSDoc contract on `claim.state`).
|
|
277
277
|
const own = activeClaims.get(params.id);
|
|
@@ -287,7 +287,7 @@ export function createModelProxy(schemaKey, registeredModelName, objectPool, syn
|
|
|
287
287
|
expiresAt: own.expiresAt,
|
|
288
288
|
};
|
|
289
289
|
}
|
|
290
|
-
return collaboration?.
|
|
290
|
+
return collaboration?.state({ model: wireModel, id: params.id }) ?? null;
|
|
291
291
|
},
|
|
292
292
|
queue(params) {
|
|
293
293
|
return {
|
package/dist/errorCodes.d.ts
CHANGED
|
@@ -37,7 +37,7 @@ import { z } from 'zod';
|
|
|
37
37
|
* code, a changed HTTP status, an envelope field. Emitted in `errors.json`
|
|
38
38
|
* and on the `Ablo-Version` response header so a consumer can detect drift.
|
|
39
39
|
*/
|
|
40
|
-
export declare const ERROR_CONTRACT_VERSION = "2026-06-
|
|
40
|
+
export declare const ERROR_CONTRACT_VERSION = "2026-06-20";
|
|
41
41
|
/** Coarse grouping for metrics dashboards and docs sectioning. */
|
|
42
42
|
export type ErrorCategory = 'auth' | 'permission' | 'capability' | 'claim' | 'conflict' | 'validation' | 'not_found' | 'tenant' | 'schema' | 'claim' | 'bootstrap' | 'transport' | 'rate_limit' | 'server' | 'client';
|
|
43
43
|
/**
|
|
@@ -239,8 +239,10 @@ export declare const ERROR_CODES: {
|
|
|
239
239
|
readonly ws_not_ready: ErrorCodeSpec;
|
|
240
240
|
readonly quota_exceeded: ErrorCodeSpec;
|
|
241
241
|
readonly connection_limit_exceeded: ErrorCodeSpec;
|
|
242
|
+
readonly rate_limit_exceeded: ErrorCodeSpec;
|
|
242
243
|
readonly internal_error: ErrorCodeSpec;
|
|
243
244
|
readonly quota_lookup_failed: ErrorCodeSpec;
|
|
245
|
+
readonly rate_limiter_unavailable: ErrorCodeSpec;
|
|
244
246
|
readonly turn_open_failed: ErrorCodeSpec;
|
|
245
247
|
readonly turn_close_failed: ErrorCodeSpec;
|
|
246
248
|
readonly invalid_options: ErrorCodeSpec;
|
package/dist/errorCodes.js
CHANGED
|
@@ -37,7 +37,7 @@ import { z } from 'zod';
|
|
|
37
37
|
* code, a changed HTTP status, an envelope field. Emitted in `errors.json`
|
|
38
38
|
* and on the `Ablo-Version` response header so a consumer can detect drift.
|
|
39
39
|
*/
|
|
40
|
-
export const ERROR_CONTRACT_VERSION = '2026-06-
|
|
40
|
+
export const ERROR_CONTRACT_VERSION = '2026-06-20';
|
|
41
41
|
/**
|
|
42
42
|
* The closed taxonomy of *how a failure recovers* — one rung above the raw
|
|
43
43
|
* `code`. Where `code` says **what** went wrong, `RecoveryClass` says **what
|
|
@@ -258,9 +258,18 @@ export const ERROR_CODES = {
|
|
|
258
258
|
// ── quota / rate limit (429) ──────────────────────────────────────
|
|
259
259
|
quota_exceeded: wire('rate_limit', 429, true, 'The organization exceeded its configured usage quota.'),
|
|
260
260
|
connection_limit_exceeded: wire('rate_limit', 429, true, 'Too many concurrent WebSocket connections for this principal or organization. Close idle connections, or retry once others drain.'),
|
|
261
|
+
// Per-CREDENTIAL request-rate limit — the fast (RPS/burst) axis, distinct from
|
|
262
|
+
// the slow-axis `quota_exceeded` (org daily/monthly usage). Keyed per API key,
|
|
263
|
+
// so one noisy key backs off without affecting the rest of the org. The
|
|
264
|
+
// `Retry-After` header carries the bucket-refill delay.
|
|
265
|
+
rate_limit_exceeded: wire('rate_limit', 429, true, 'This API key is sending requests too quickly; slow down and retry after the indicated delay.'),
|
|
261
266
|
// ── server (5xx) ───────────────────────────────────────────────────
|
|
262
267
|
internal_error: wire('server', 500, true, 'An unexpected server error occurred.'),
|
|
263
268
|
quota_lookup_failed: wire('server', 503, true, 'The quota decision could not be loaded.'),
|
|
269
|
+
// The per-key rate-limiter backend (Redis) was unreachable and the API is
|
|
270
|
+
// configured to FAIL CLOSED on that path, so the request was rejected rather
|
|
271
|
+
// than admitted unchecked. Retryable: the next attempt re-probes the backend.
|
|
272
|
+
rate_limiter_unavailable: wire('server', 503, true, 'The rate-limiter backend is unavailable and this endpoint is configured to fail closed; retry shortly.'),
|
|
264
273
|
turn_open_failed: wire('server', 500, true, 'The agent turn failed to open.'),
|
|
265
274
|
turn_close_failed: wire('server', 500, true, 'The agent turn failed to close cleanly.'),
|
|
266
275
|
// ── client-only invariants (never serialized) ──────────────────────
|
package/dist/schema/index.d.ts
CHANGED
|
@@ -23,13 +23,13 @@
|
|
|
23
23
|
export { z } from 'zod';
|
|
24
24
|
export { field, indexed, getFieldMeta, type FieldBuilder, type FieldMeta } from './field.js';
|
|
25
25
|
export { relation, type RelationDef, type RelationType } from './relation.js';
|
|
26
|
-
export { tenancySchema, scopedViaRefSchema, resolveTenancy, tenancyColumn, DEFAULT_ORG_COLUMN, type Tenancy, type ScopedViaRef, type
|
|
26
|
+
export { tenancySchema, scopedViaRefSchema, policyInputSchema, resolvePolicy, resolveTenancy, tenancyColumn, DEFAULT_ORG_COLUMN, type Tenancy, type ScopedViaRef, type PolicyInput, } from './tenancy.js';
|
|
27
27
|
export { planeSchema, DEFAULT_PLANE, type SchemaPlane } from './plane.js';
|
|
28
28
|
export { syncDeltaCoreSchema, deltaAttributionSchema, deltaProvenanceSchema, syncDeltaRowSchema, participantKindSchema, confirmationStateSchema, backfillProvenanceSchema, DELTA_PLANES, type SyncDeltaCore, type DeltaAttribution, type DeltaProvenance, type SyncDeltaRow, type ParticipantKind, type ConfirmationState, type BackfillProvenance, } from './sync-delta-row.js';
|
|
29
29
|
export { syncDeltaActionSchema, wireDeltaDataSchema, participantRefSchema, syncDeltaWireCoreSchema, clientSyncDeltaSchema, serverSyncDeltaSchema, type SyncDeltaAction, type WireDeltaData, type ParticipantRef, type SyncDeltaWireCore, type ClientSyncDelta, type ServerSyncDelta, } from './sync-delta-wire.js';
|
|
30
30
|
export { model, scopeKindOf, type ModelDef, type ModelOptions, type LoadStrategy, type PersistOptions, type RelationRecord, type GrantsRef, } from './model.js';
|
|
31
31
|
export { mutable, readOnly, type SugarOptions } from './sugar.js';
|
|
32
|
-
export { defineSchema, composeIdentitySyncGroups, type Schema, type SchemaRecord, type Model, type InferModel, type InferCreate, type InferModelNames, type BaseModelFields, type InsertValue, type UpsertValue, type UpdateValue, type DeleteId, type DefineSchemaOptions, type Casing, type CasingConvention, type CasingFn, composeEntitySyncGroups, type IdentityRole, type IdentityContext, type IdentityRoleSource, type EntityRole, type EntityContext, type EntityRoleSource, type RoleSource, type RoleContext, type SyncGroup, type SyncGroupInput, identityRole, entityRole, extractIdentityIds, extractEntityIds, syncGroup, syncGroupSchema, syncGroupInputSchema, isSyncGroupInput, identityRoleSchema, entityRoleSchema, roleSchema, roleSourceSchema, scopeSchema, grantsRefSchema, } from './schema.js';
|
|
32
|
+
export { defineSchema, composeIdentitySyncGroups, type Schema, type SchemaRecord, type Model, type InferModel, type InferCreate, type InferModelNames, type BaseModelFields, type InsertValue, type UpsertValue, type UpdateValue, type DeleteId, type DefineSchemaOptions, type Casing, type CasingConvention, type CasingFn, composeEntitySyncGroups, type IdentityRole, type IdentityContext, type IdentityRoleSource, type EntityRole, type EntityContext, type EntityRoleSource, type RoleSource, type RoleContext, type SyncGroup, type SyncGroupInput, identityRole, entityRole, extractIdentityIds, extractEntityIds, syncGroup, syncGroupSchema, syncGroupInputSchema, isSyncGroupInput, identityRoleSchema, entityRoleSchema, roleSchema, roleSourceSchema, scopeSchema, grantsRefSchema, groupsInputSchema, type GroupsInput, } from './schema.js';
|
|
33
33
|
export { serializeSchema, parseSchema, toSchemaJSON, fromSchemaJSON, schemaHash, type SchemaJSON, type ModelJSON, type RelationJSON, } from './serialize.js';
|
|
34
34
|
export { selectModels } from './select.js';
|
|
35
35
|
export { generateProvisionPlan, generateMigrationPlan, appSchemaName, camelToSnake, snakeToCamel, q, sqlType, type ProvisionPlan, type MigrationPlan, } from './ddl.js';
|