@elevasis/sdk 1.49.0 → 1.50.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/chunk-MGZZ4HL4.js +4399 -0
- package/dist/chunk-VYWGWJRW.js +130 -0
- package/dist/chunk-YJDXRHNP.js +7901 -0
- package/dist/cli.cjs +208 -51
- package/dist/index.d.ts +347 -2
- package/dist/index.js +2 -7831
- package/dist/node/index.d.ts +3 -3759
- package/dist/node/index.js +2 -124
- package/dist/test-utils/index.d.ts +2 -12664
- package/dist/test-utils/index.js +93 -28073
- package/dist/worker/index.d.ts +532 -12872
- package/dist/worker/index.js +3 -7509
- package/package.json +10 -2
- package/reference/packages/core/src/content/README.md +4 -1
- package/reference/rules/agent-start-here.md +1 -1
- package/reference/rules/frontend.md +3 -1
- package/reference/rules/package-taxonomy.md +7 -5
- package/reference/rules/ui.md +30 -4
- package/reference/scaffold/recipes/gate-by-feature-or-admin.md +8 -6
- package/reference/scaffold/ui/feature-flags-and-gating.md +11 -1
- package/reference/sdk/cli-management.mdx +127 -125
- package/reference/sdk/cli.mdx +70 -61
- package/reference/sdk/deployment/index.mdx +23 -6
- package/reference/sdk/platform-tools/adapters-platform.mdx +1 -1
- package/reference/sdk/troubleshooting.mdx +72 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elevasis/sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.50.0",
|
|
4
4
|
"description": "SDK for building Elevasis organization resources",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
"dist/test-utils/index.js",
|
|
33
33
|
"dist/test-utils/index.d.ts",
|
|
34
34
|
"dist/node/",
|
|
35
|
+
"dist/chunk-*.js",
|
|
35
36
|
"dist/cli.cjs",
|
|
36
37
|
"reference/"
|
|
37
38
|
],
|
|
@@ -41,8 +42,14 @@
|
|
|
41
42
|
"remark-gfm": "^4.0.1"
|
|
42
43
|
},
|
|
43
44
|
"peerDependencies": {
|
|
45
|
+
"vitest": ">=3",
|
|
44
46
|
"zod": "^4.1.0"
|
|
45
47
|
},
|
|
48
|
+
"peerDependenciesMeta": {
|
|
49
|
+
"vitest": {
|
|
50
|
+
"optional": true
|
|
51
|
+
}
|
|
52
|
+
},
|
|
46
53
|
"devDependencies": {
|
|
47
54
|
"@types/node": "^22.0.0",
|
|
48
55
|
"chalk": "^5.3.0",
|
|
@@ -54,8 +61,9 @@
|
|
|
54
61
|
"rollup-plugin-dts": "^6.3.0",
|
|
55
62
|
"tsup": "^8.0.0",
|
|
56
63
|
"typescript": "5.9.2",
|
|
64
|
+
"vitest": "^3.2.4",
|
|
57
65
|
"zod": "^4.1.0",
|
|
58
|
-
"@repo/core": "0.
|
|
66
|
+
"@repo/core": "0.65.0",
|
|
59
67
|
"@repo/eslint-config": "0.0.0",
|
|
60
68
|
"@repo/typescript-config": "0.0.0"
|
|
61
69
|
},
|
|
@@ -16,7 +16,10 @@ The helpers live here rather than in `@repo/ui` because they have three consumer
|
|
|
16
16
|
| `ContentDistributionResponseSchema`, `CreateContentDistributionRequestSchema`, `UpdateContentDistributionRequestSchema` | `content_distributions` transport shape, including the manual-publish fields (`publishMethod`, `platformPostId`, `platformUrl`). |
|
|
17
17
|
| `ContentItemSourceAssetResponseSchema`, `ContentItemSourceAssetInputSchema`, `UpdateContentItemSourceAssetRequestSchema`, `ReorderContentItemSourceAssetsRequestSchema`, `ContentAssetCropSchema` | `content_item_source_assets` transport shape — an item's ordered source assets, `position` 0 is the cover. `crop` is per-MEMBERSHIP normalized fractions, because the same photo crops differently in different items. The initial set rides `CreateContentItemRequestSchema.sourceAssets`; every later mutation is its own method, and `UpdateContentItemRequestSchema` deliberately never touches membership. `derivativePath` / `derivativeCrop` / `derivativeRenderedAt` carry the rendered crop: a stored JPEG in the `content-derivatives` bucket, so the cropped image exists as a file rather than only as CSS. Stale is derived — the two crops differ — never stored as a flag. |
|
|
18
18
|
| `PlatformContent`, `YouTubeContent`, `LinkedInContent`, `InstagramContent`, `XContent`, `AnyPlatformContent`, `PlatformContentMap`, `Platform` | Typed interfaces for platform-specific content stored in `content_distributions.platform_content`. |
|
|
19
|
-
| `isOpenContentReviewGate`, `getOpenContentReviewGates`
|
|
19
|
+
| `isOpenContentReviewGate`, `getOpenContentReviewGates`, `OpenContentReviewGate` | The one predicate deciding whether a `processing_state` entry is an open, unreviewed gate on a **queue-reviewed** step — `step.reviewVenue === 'queue'`, entry not `running`, and `executionId` still non-null. It answers "can the queue screen clear this", so a Workspace-reviewed step has no gate here. Consumed by the `/queue` handler, `reviewItem`'s `stepKey` validation, and the review page. |
|
|
20
|
+
| `resolveCurrentContentStep` | The step an item is sitting at: steps ascending by `order` (ties by `key`), first one not yet `'success'`, falling back to the last step once all have succeeded. Returns `undefined` for no declared steps and for an unplaced item whose `processingState` names no key the current catalog declares. Duplicates `deriveItemPlacement`'s ordering rule by hand — keep the two in sync. |
|
|
21
|
+
| `isAwaitingReview` | Cheap boolean for counting and filtering: is a person owed an action on this item. True when the step's `reviewMode` is not `'none'` and `item.reviewedAt` is still null. Takes whatever step the caller already resolved, typically via `resolveCurrentContentStep`. |
|
|
22
|
+
| `resolveReviewTarget`, `ContentReviewTarget` | The exhaustive answer surfaces render from — **where** the review happens, not just whether one is owed. An open queue gate wins first, then a current step with `reviewVenue === 'workspace'` that `isAwaitingReview` plus a resolved route, else `{ kind: 'none' }`. Deliberately separate from `isAwaitingReview` so callers stop recombining "is one owed" and "where" with `&&` at the point of use. |
|
|
20
23
|
| `deriveContentBoard`, `ContentBoard`, `ContentBoardCard`, `ContentBoardColumn`, `ContentBoardGate` | Placement: `(items, pipeline, now)` to columns, gates, waiting counts, plus the `done` and `unplaced` buckets. Pure. Consumed by the Command Center board and `elevasis-sdk content:board`. |
|
|
21
24
|
| `getContentItemIdentity` | Names an item for display when `title` cannot — first non-blank line of `body`, then the first string payload field, then `title`, then `'Untitled'`. |
|
|
22
25
|
|
|
@@ -109,7 +109,7 @@ Then inspect:
|
|
|
109
109
|
|
|
110
110
|
- `core/config/organization-model.ts` -- Organization Model overrides plus Systems and Resources descriptors. Start here for feature labels, surface mapping, and resource identity/governance changes.
|
|
111
111
|
- `ui/src/routes/__root.tsx` and the relevant nav config files
|
|
112
|
-
- Access-gating call sites: `useAccess(accessKey)` from `@elevasis/ui/hooks` and `<AccessGuard accessKey={...}>` from `@elevasis/ui/auth`. The template ships no local `useFeatureAccess.ts` and there is no hook factory -- these two published entry points are the whole gating surface.
|
|
112
|
+
- Access-gating call sites: `useAccess(accessKey)` from `@elevasis/ui/hooks` and `<AccessGuard accessKey={...}>` from `@elevasis/ui/auth`. The template ships no local `useFeatureAccess.ts` and there is no hook factory -- these two published entry points are the whole gating surface. A System's top-level route file wraps that pairing in `<ProtectedSystemRoute accessKey={...}>` from `@elevasis/ui/features/auth`, which is what every template System route ships; see `ui.md` "System route guarding".
|
|
113
113
|
|
|
114
114
|
Verify with: published docs for `@elevasis/core/organization-model`, plus current scaffold routes and manifests.
|
|
115
115
|
|
|
@@ -14,7 +14,9 @@ paths:
|
|
|
14
14
|
- `ElevasisUIProvider` in `ui/src/main.tsx` auto-composes shared UI, auth, and API surface -- route files do not wire providers manually
|
|
15
15
|
- `useApiClient()` from `@elevasis/ui/api` for authenticated API calls, passing `API_URL` from `@/lib/constants/api` -- never raw `fetch` with auth headers. There is no `ui/src/lib/hooks/` directory; see `ui.md` "API and Streaming"
|
|
16
16
|
- `routeTree.gen.ts` is auto-generated on `pnpm dev` -- never edit manually
|
|
17
|
-
- Auth protection: wrap page content with `ProtectedRoute` from `@elevasis/ui/auth`. Gate further with `AccessGuard accessKey={...}` nested inside `ProtectedRoute` -- a System path for system gating, or an `AccessKeys` constant (e.g. `AccessKeys.platformAdmin`) for permission-backed gating. `FeatureGuard`, `SystemGuard`, `SurfaceGuard`, and `AdminGuard` are retired -- do not reintroduce them
|
|
17
|
+
- Auth protection: wrap page content with `ProtectedRoute` from `@elevasis/ui/features/auth`. Gate further with `AccessGuard accessKey={...}` nested inside `ProtectedRoute` -- a System path for system gating, or an `AccessKeys` constant (e.g. `AccessKeys.platformAdmin`) for permission-backed gating. `FeatureGuard`, `SystemGuard`, `SurfaceGuard`, and `AdminGuard` are retired -- do not reintroduce them
|
|
18
|
+
- **For a System's top-level route, use `ProtectedSystemRoute accessKey={...}` from `@elevasis/ui/features/auth` instead of writing that pairing by hand.** It expands to exactly the `ProtectedRoute` + `AccessGuard` nesting above and adds `SystemUnavailableState` as the guard's `fallback`, so a denied user sees the reason rather than `AccessGuard`'s blank `fallback = null` default. Every System route in the template ships this shape; see `ui.md` "System route guarding". Keep `createFileRoute` in the route file and keep bare `AccessGuard` for gating below the route level
|
|
19
|
+
- **Two `ProtectedRoute` components exist and the subpath you import from decides what the user sees while the app initializes.** `@elevasis/ui/features/auth` is the one route files want: it wraps the base guard with an animated full-screen loader and a default `AppShellError` error fallback. `@elevasis/ui/auth` exports the headless base guard, whose `fallback` defaults to `null` -- importing it without passing a `fallback` renders a blank screen for the whole initialization window and swallows initialization errors. Reach for the headless one only when the surface renders its own loading state. `AccessKeys` is a plain constant table and stays on `@elevasis/ui/auth` (or `@elevasis/ui/hooks`) either way
|
|
18
20
|
- Never fork `@elevasis/ui` components -- if a published component needs a tweak, that missing capability is a bug in `@elevasis/ui`
|
|
19
21
|
|
|
20
22
|
## Silent-Break Gotchas
|
|
@@ -11,11 +11,13 @@ External projects consume the **published `@elevasis/*` surface only**. The Elev
|
|
|
11
11
|
|
|
12
12
|
## What You Can Import
|
|
13
13
|
|
|
14
|
-
| Package | Subpaths
|
|
15
|
-
| ---------------- |
|
|
16
|
-
| `@elevasis/sdk` | default, `/worker`, `/node`, `/test-utils`
|
|
17
|
-
| `@elevasis/ui` | default, `/auth`, `/initialization`, etc.
|
|
18
|
-
| `@elevasis/core` | `/organization-model`, `/entities`, `/
|
|
14
|
+
| Package | Subpaths | Where it lives in `node_modules` |
|
|
15
|
+
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- |
|
|
16
|
+
| `@elevasis/sdk` | default, `/worker`, `/node`, `/test-utils` | `node_modules/@elevasis/sdk/` |
|
|
17
|
+
| `@elevasis/ui` | default, `/auth`, `/initialization`, etc. | `node_modules/@elevasis/ui/` |
|
|
18
|
+
| `@elevasis/core` | default, `/auth`, `/test-utils`, `/organization-model`, `/organization-model/readiness`, `/content`, `/entities`, `/knowledge` | `node_modules/@elevasis/core/` |
|
|
19
|
+
|
|
20
|
+
`@elevasis/core`'s eight subpaths are its `publishConfig.exports`, and that is the whole published surface -- there is no `/utils`. `/content` is where `deriveContentBoard`, `resolveReviewTarget`, `isAwaitingReview`, and the content item and source-asset schemas live, so an import from it is legitimate.
|
|
19
21
|
|
|
20
22
|
## What You Will See in Monorepo Docs (and should NOT import)
|
|
21
23
|
|
package/reference/rules/ui.md
CHANGED
|
@@ -60,10 +60,12 @@ The app uses WorkOS AuthKit through `ElevasisUIProvider`. Authentication is enfo
|
|
|
60
60
|
|
|
61
61
|
**Route protection:**
|
|
62
62
|
|
|
63
|
-
Wrap protected route components with `ProtectedRoute` from `@elevasis/ui/auth`. For
|
|
63
|
+
Wrap protected route components with `ProtectedRoute` from `@elevasis/ui/features/auth`. For a **System's top-level route**, use `ProtectedSystemRoute` instead -- it is the canonical shape and is what `ui/src/routes/crm.tsx`, `lead-gen.tsx`, `monitoring.tsx`, `operations.tsx`, `projects.tsx`, `content.tsx`, and `knowledge.tsx` all ship as. See "System route guarding" below. `ProtectedRoute` on its own is for a protected route that gates on authentication alone:
|
|
64
|
+
|
|
65
|
+
`@elevasis/ui` publishes two components under this name and they behave differently. The one route files want is `@elevasis/ui/features/auth`: it wraps the base guard with an animated full-screen loader that fades out when initialization completes, and supplies a default `AppShellError` fallback so an initialization failure renders a retry surface instead of nothing. `@elevasis/ui/auth` exports the headless base guard, whose `fallback` prop defaults to `null` -- a route that imports it and passes no `fallback` shows a blank screen for the entire initialization window and silently swallows blocking errors. Import the headless one only when the surface deliberately renders its own loading state.
|
|
64
66
|
|
|
65
67
|
```tsx
|
|
66
|
-
import { ProtectedRoute } from '@elevasis/ui/auth'
|
|
68
|
+
import { ProtectedRoute } from '@elevasis/ui/features/auth'
|
|
67
69
|
|
|
68
70
|
function HomePageGuarded() {
|
|
69
71
|
return (
|
|
@@ -74,14 +76,38 @@ function HomePageGuarded() {
|
|
|
74
76
|
}
|
|
75
77
|
```
|
|
76
78
|
|
|
79
|
+
**System route guarding:**
|
|
80
|
+
|
|
81
|
+
`ProtectedSystemRoute` from `@elevasis/ui/features/auth` is the shape a System's top-level route file uses. It composes `ProtectedRoute` + `AccessGuard` and supplies `SystemUnavailableState` as the guard's `fallback`, so a denial renders the specific reason `checkAccess` already computed instead of `AccessGuard`'s own blank `fallback = null` default:
|
|
82
|
+
|
|
83
|
+
```tsx
|
|
84
|
+
import { ProtectedSystemRoute } from '@elevasis/ui/features/auth'
|
|
85
|
+
import { createFileRoute, Outlet } from '@tanstack/react-router'
|
|
86
|
+
|
|
87
|
+
export const Route = createFileRoute('/crm')({
|
|
88
|
+
component: CrmLayout
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
function CrmLayout() {
|
|
92
|
+
return (
|
|
93
|
+
<ProtectedSystemRoute accessKey="sales.crm">
|
|
94
|
+
<Outlet />
|
|
95
|
+
</ProtectedSystemRoute>
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Three things this does not change. `createFileRoute` stays in the route file -- TanStack's file-based routing requires it there, and `ProtectedSystemRoute` only replaces the route's layout body. `AccessGuard` is unchanged and is still what you reach for below the route level, or for a permission-backed `AccessKeys` gate that is not a System path. And a leaf page under an already-guarded layout route needs no guard of its own -- the layout's guard covers it.
|
|
101
|
+
|
|
77
102
|
**Access gating:**
|
|
78
103
|
|
|
79
104
|
Access is resolved by the unified Access Model -- System lifecycle from the organization model, role permissions, diagnostic allowlists, membership scope, and platform-admin bypass -- through two published symbols. There is no `createFeatureAccessHook` and no `SystemGuard`; both are retired and must not be reintroduced.
|
|
80
105
|
|
|
81
|
-
`AccessGuard` from `@elevasis/ui/auth` wraps a subtree:
|
|
106
|
+
`AccessGuard` from `@elevasis/ui/auth` wraps a subtree. Prefer `ProtectedSystemRoute` above when the subtree is a whole System's route; the hand-written pairing below is what it expands to, and is still correct for non-System gating:
|
|
82
107
|
|
|
83
108
|
```tsx
|
|
84
|
-
import {
|
|
109
|
+
import { AccessKeys } from '@elevasis/ui/auth'
|
|
110
|
+
import { AccessGuard, ProtectedRoute } from '@elevasis/ui/features/auth'
|
|
85
111
|
|
|
86
112
|
<ProtectedRoute>
|
|
87
113
|
<AccessGuard accessKey="sales.crm">
|
|
@@ -52,23 +52,25 @@ Dotted IDs such as `analytics.reports` inherit lifecycle and shell placement fro
|
|
|
52
52
|
|
|
53
53
|
## Route-level system gate
|
|
54
54
|
|
|
55
|
+
`ProtectedSystemRoute` is the shape a System's top-level route uses. It composes `ProtectedRoute` + `AccessGuard` and supplies `SystemUnavailableState` as the fallback, so a denial renders the reason `checkAccess` computed instead of a blank screen:
|
|
56
|
+
|
|
55
57
|
```tsx
|
|
56
|
-
import {
|
|
58
|
+
import { ProtectedSystemRoute } from '@elevasis/ui/features/auth'
|
|
57
59
|
import { createFileRoute, Outlet } from '@tanstack/react-router'
|
|
58
60
|
|
|
59
61
|
export const Route = createFileRoute('/analytics')({ component: AnalyticsLayout })
|
|
60
62
|
|
|
61
63
|
function AnalyticsLayout() {
|
|
62
64
|
return (
|
|
63
|
-
<
|
|
64
|
-
<
|
|
65
|
-
|
|
66
|
-
</AccessGuard>
|
|
67
|
-
</ProtectedRoute>
|
|
65
|
+
<ProtectedSystemRoute accessKey="analytics">
|
|
66
|
+
<Outlet />
|
|
67
|
+
</ProtectedSystemRoute>
|
|
68
68
|
)
|
|
69
69
|
}
|
|
70
70
|
```
|
|
71
71
|
|
|
72
|
+
Writing the pairing by hand is still correct and is exactly what the above expands to. Reach for it when the gate is not a whole System's route -- see the admin-only recipe below.
|
|
73
|
+
|
|
72
74
|
The sidebar is derived from `OrganizationModel.systems`; hiding a node there is display behavior only. Keep route guards in place for direct URL access.
|
|
73
75
|
|
|
74
76
|
## Admin-only route
|
|
@@ -18,7 +18,17 @@ The shell derives visible sidebar entries from `shellModel.topLevel()` and `shel
|
|
|
18
18
|
|
|
19
19
|
## Route Guards
|
|
20
20
|
|
|
21
|
-
Navigation visibility is cosmetic. Always guard routes directly:
|
|
21
|
+
Navigation visibility is cosmetic. Always guard routes directly. For a System's top-level route use `ProtectedSystemRoute`, which composes `ProtectedRoute` + `AccessGuard` and renders `SystemUnavailableState` on denial instead of a blank fallback:
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
import { ProtectedSystemRoute } from '@elevasis/ui/features/auth'
|
|
25
|
+
|
|
26
|
+
<ProtectedSystemRoute accessKey="sales.crm">
|
|
27
|
+
<Outlet />
|
|
28
|
+
</ProtectedSystemRoute>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
That expands to the pairing below, which stays correct for gating that is not a whole System's route:
|
|
22
32
|
|
|
23
33
|
```tsx
|
|
24
34
|
import { AccessGuard } from '@elevasis/ui/auth'
|