@pylonsync/sdk 0.3.385 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +8 -1
- package/package.json +1 -1
- package/src/index.ts +44 -2
- package/src/sdk.types.test.ts +36 -1
package/dist/index.d.ts
CHANGED
|
@@ -769,6 +769,12 @@ export declare function policiesToManifest(policies: PolicyDefinition[]): Manife
|
|
|
769
769
|
* })
|
|
770
770
|
*/
|
|
771
771
|
export type AuthConfig = {
|
|
772
|
+
/**
|
|
773
|
+
* Additional organization role slugs. Built-ins (`owner`, `admin`,
|
|
774
|
+
* `member`) remain available. Custom roles are exact-match labels and do
|
|
775
|
+
* not inherit member/admin permissions.
|
|
776
|
+
*/
|
|
777
|
+
orgRoles?: string[];
|
|
772
778
|
user?: {
|
|
773
779
|
/** Manifest entity name pylon treats as the User table. Default `"User"`. */
|
|
774
780
|
entity?: string;
|
|
@@ -953,6 +959,7 @@ export type ManifestAuthConfig = {
|
|
|
953
959
|
invite_entity: string;
|
|
954
960
|
disabled: boolean;
|
|
955
961
|
};
|
|
962
|
+
org_roles: string[];
|
|
956
963
|
trusted_origins: string[];
|
|
957
964
|
};
|
|
958
965
|
/**
|
|
@@ -971,7 +978,7 @@ export declare function buildManifest(options: {
|
|
|
971
978
|
queries?: QueryDefinition[];
|
|
972
979
|
actions?: ActionDefinition[];
|
|
973
980
|
policies?: PolicyDefinition[];
|
|
974
|
-
auth?: ManifestAuthConfig;
|
|
981
|
+
auth?: ManifestAuthConfig | AuthConfig;
|
|
975
982
|
llm?: ManifestLlmConfig;
|
|
976
983
|
connections?: ManifestConnection[];
|
|
977
984
|
crons?: ManifestCron[];
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -1408,6 +1408,12 @@ export function policiesToManifest(
|
|
|
1408
1408
|
* })
|
|
1409
1409
|
*/
|
|
1410
1410
|
export type AuthConfig = {
|
|
1411
|
+
/**
|
|
1412
|
+
* Additional organization role slugs. Built-ins (`owner`, `admin`,
|
|
1413
|
+
* `member`) remain available. Custom roles are exact-match labels and do
|
|
1414
|
+
* not inherit member/admin permissions.
|
|
1415
|
+
*/
|
|
1416
|
+
orgRoles?: string[];
|
|
1411
1417
|
user?: {
|
|
1412
1418
|
/** Manifest entity name pylon treats as the User table. Default `"User"`. */
|
|
1413
1419
|
entity?: string;
|
|
@@ -1621,9 +1627,34 @@ export type ManifestAuthConfig = {
|
|
|
1621
1627
|
invite_entity: string;
|
|
1622
1628
|
disabled: boolean;
|
|
1623
1629
|
};
|
|
1630
|
+
org_roles: string[];
|
|
1624
1631
|
trusted_origins: string[];
|
|
1625
1632
|
};
|
|
1626
1633
|
|
|
1634
|
+
const BUILTIN_ORG_ROLES = new Set(["owner", "admin", "member"]);
|
|
1635
|
+
const ORG_ROLE_PATTERN = /^[a-z][a-z0-9_-]{0,63}$/;
|
|
1636
|
+
|
|
1637
|
+
function validateOrgRoles(roles: string[]): string[] {
|
|
1638
|
+
const seen = new Set<string>();
|
|
1639
|
+
return roles.map((role) => {
|
|
1640
|
+
if (!ORG_ROLE_PATTERN.test(role)) {
|
|
1641
|
+
throw new Error(
|
|
1642
|
+
`Invalid organization role "${role}". Roles must match ${ORG_ROLE_PATTERN}`,
|
|
1643
|
+
);
|
|
1644
|
+
}
|
|
1645
|
+
if (BUILTIN_ORG_ROLES.has(role)) {
|
|
1646
|
+
throw new Error(
|
|
1647
|
+
`Organization role "${role}" is built in and must not be redeclared`,
|
|
1648
|
+
);
|
|
1649
|
+
}
|
|
1650
|
+
if (seen.has(role)) {
|
|
1651
|
+
throw new Error(`Duplicate organization role "${role}"`);
|
|
1652
|
+
}
|
|
1653
|
+
seen.add(role);
|
|
1654
|
+
return role;
|
|
1655
|
+
});
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1627
1658
|
/**
|
|
1628
1659
|
* Build the manifest's `auth` block from the user-facing camelCase
|
|
1629
1660
|
* config. Translates to the snake_case shape the Rust runtime expects.
|
|
@@ -1653,10 +1684,17 @@ export function auth(cfg: AuthConfig = {}): ManifestAuthConfig {
|
|
|
1653
1684
|
invite_entity: cfg.org?.inviteEntity ?? "OrgInvite",
|
|
1654
1685
|
disabled: cfg.org?.disabled ?? false,
|
|
1655
1686
|
},
|
|
1687
|
+
org_roles: validateOrgRoles(cfg.orgRoles ?? []),
|
|
1656
1688
|
trusted_origins: cfg.trustedOrigins ?? [],
|
|
1657
1689
|
};
|
|
1658
1690
|
}
|
|
1659
1691
|
|
|
1692
|
+
function isManifestAuthConfig(
|
|
1693
|
+
cfg: ManifestAuthConfig | AuthConfig,
|
|
1694
|
+
): cfg is ManifestAuthConfig {
|
|
1695
|
+
return "trusted_origins" in cfg;
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1660
1698
|
export function buildManifest(options: {
|
|
1661
1699
|
name: string;
|
|
1662
1700
|
version: string;
|
|
@@ -1665,7 +1703,7 @@ export function buildManifest(options: {
|
|
|
1665
1703
|
queries?: QueryDefinition[];
|
|
1666
1704
|
actions?: ActionDefinition[];
|
|
1667
1705
|
policies?: PolicyDefinition[];
|
|
1668
|
-
auth?: ManifestAuthConfig;
|
|
1706
|
+
auth?: ManifestAuthConfig | AuthConfig;
|
|
1669
1707
|
llm?: ManifestLlmConfig;
|
|
1670
1708
|
connections?: ManifestConnection[];
|
|
1671
1709
|
crons?: ManifestCron[];
|
|
@@ -1707,7 +1745,11 @@ export function buildManifest(options: {
|
|
|
1707
1745
|
queries: queriesToManifest(options.queries ?? []),
|
|
1708
1746
|
actions: actionsToManifest(options.actions ?? []),
|
|
1709
1747
|
policies: policiesToManifest(allPolicies),
|
|
1710
|
-
auth: options.auth
|
|
1748
|
+
auth: options.auth
|
|
1749
|
+
? isManifestAuthConfig(options.auth)
|
|
1750
|
+
? options.auth
|
|
1751
|
+
: auth(options.auth)
|
|
1752
|
+
: auth(),
|
|
1711
1753
|
...(options.llm && Object.keys(options.llm).length > 0
|
|
1712
1754
|
? { llm: options.llm }
|
|
1713
1755
|
: {}),
|
package/src/sdk.types.test.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import { buildManifest, entity, field } from "./index";
|
|
2
|
+
import { auth, buildManifest, entity, field } from "./index";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* The shape callers actually hit.
|
|
@@ -67,3 +67,38 @@ describe("entity() sync option", () => {
|
|
|
67
67
|
.toBe(false);
|
|
68
68
|
});
|
|
69
69
|
});
|
|
70
|
+
|
|
71
|
+
describe("custom organization roles", () => {
|
|
72
|
+
const base = {
|
|
73
|
+
name: "roles",
|
|
74
|
+
version: "1",
|
|
75
|
+
entities: [],
|
|
76
|
+
policies: [],
|
|
77
|
+
routes: [],
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
test("buildManifest accepts the concise auth.orgRoles form", () => {
|
|
81
|
+
const manifest = buildManifest({
|
|
82
|
+
...base,
|
|
83
|
+
auth: { orgRoles: ["reviewer", "speaker_manager"] },
|
|
84
|
+
});
|
|
85
|
+
expect(manifest.auth?.org_roles).toEqual([
|
|
86
|
+
"reviewer",
|
|
87
|
+
"speaker_manager",
|
|
88
|
+
]);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test("the auth() helper emits the same role allowlist", () => {
|
|
92
|
+
expect(auth({ orgRoles: ["billing"] }).org_roles).toEqual(["billing"]);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("invalid, duplicate, and redeclared built-in roles fail clearly", () => {
|
|
96
|
+
expect(() => auth({ orgRoles: ["Reviewer"] })).toThrow(
|
|
97
|
+
"Invalid organization role",
|
|
98
|
+
);
|
|
99
|
+
expect(() => auth({ orgRoles: ["reviewer", "reviewer"] })).toThrow(
|
|
100
|
+
"Duplicate organization role",
|
|
101
|
+
);
|
|
102
|
+
expect(() => auth({ orgRoles: ["member"] })).toThrow("is built in");
|
|
103
|
+
});
|
|
104
|
+
});
|