rahman-resources 1.12.1 → 1.13.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/lib/contract-types.ts +15 -35
- package/lib/contract-validate.ts +12 -21
- package/lib/contract.ts +2 -4
- package/lib/manifest.json +10 -10
- package/package.json +1 -1
package/lib/contract-types.ts
CHANGED
|
@@ -91,31 +91,20 @@ export interface SliceContractProvides {
|
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
// ---------------------------------------------------------------------------
|
|
94
|
-
//
|
|
94
|
+
// Generalisation gate
|
|
95
95
|
// ---------------------------------------------------------------------------
|
|
96
|
+
// (Formerly nested inside the Wave N+3 `bidir` block. BSDL was removed in
|
|
97
|
+
// Sesi 2; the sync policy died with it, but the generalisation contract is
|
|
98
|
+
// still consumed by scripts/validation/check-forbidden-terms.mjs — so it
|
|
99
|
+
// lives on as a top-level field.)
|
|
96
100
|
|
|
97
101
|
/**
|
|
98
|
-
*
|
|
102
|
+
* Portability level of the slice source.
|
|
99
103
|
*
|
|
100
|
-
* - `
|
|
101
|
-
*
|
|
102
|
-
* the kitab. Reserved for slices with strict generalisation gates.
|
|
103
|
-
* - `notify`: surface in the scan report; no auto-action.
|
|
104
|
-
* - `manual`: default — operator picks up via `/rr-prep` + `/rr-send`.
|
|
105
|
-
* - `frozen`: kitab refuses both UP and DOWN sync. Lock for retired slices.
|
|
106
|
-
*/
|
|
107
|
-
export type SliceSyncPolicy = "auto-pr" | "notify" | "manual" | "frozen";
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Generalisation level a consumer-side `.kitab.json` MUST claim before
|
|
111
|
-
* `rr-send` accepts the push back into the kitab.
|
|
112
|
-
*
|
|
113
|
-
* - `portable`: no consumer-specific business terms baked in. UP-sync allowed.
|
|
114
|
-
* - `needs-adapter`: requires a thin adapter wired by the consumer; UP-sync
|
|
115
|
-
* blocked until blockers are addressed (or the contract drops the slice
|
|
116
|
-
* to `consumer-locked`).
|
|
104
|
+
* - `portable`: no consumer-specific business terms baked in.
|
|
105
|
+
* - `needs-adapter`: requires a thin adapter wired by the consumer.
|
|
117
106
|
* - `consumer-locked`: contains business-specific logic that cannot be
|
|
118
|
-
* generalised.
|
|
107
|
+
* generalised.
|
|
119
108
|
*/
|
|
120
109
|
export type GeneralizationLevel =
|
|
121
110
|
| "portable"
|
|
@@ -123,14 +112,15 @@ export type GeneralizationLevel =
|
|
|
123
112
|
| "consumer-locked";
|
|
124
113
|
|
|
125
114
|
/**
|
|
126
|
-
* Generalisation contract — what the
|
|
127
|
-
*
|
|
115
|
+
* Generalisation contract — what the forbidden-terms audit scans for, and
|
|
116
|
+
* which props the consumer MUST inject.
|
|
128
117
|
*/
|
|
129
118
|
export interface SliceGeneralization {
|
|
130
119
|
level: GeneralizationLevel;
|
|
131
120
|
/**
|
|
132
121
|
* Identifiers / business terms that MUST NOT appear in the slice source
|
|
133
|
-
* tree.
|
|
122
|
+
* tree. check-forbidden-terms.mjs scans .ts/.tsx files. Empty when the
|
|
123
|
+
* slice is generic.
|
|
134
124
|
*/
|
|
135
125
|
forbiddenTerms?: string[];
|
|
136
126
|
/**
|
|
@@ -140,16 +130,6 @@ export interface SliceGeneralization {
|
|
|
140
130
|
requiredProps?: string[];
|
|
141
131
|
}
|
|
142
132
|
|
|
143
|
-
/**
|
|
144
|
-
* Bidirectional sync block. Optional, additive — slices without it default to
|
|
145
|
-
* `{ syncPolicy: "manual", generalization: { level: "portable" } }` for
|
|
146
|
-
* legacy compatibility with Wave N+1 contracts.
|
|
147
|
-
*/
|
|
148
|
-
export interface SliceBidirContract {
|
|
149
|
-
syncPolicy: SliceSyncPolicy;
|
|
150
|
-
generalization: SliceGeneralization;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
133
|
// ---------------------------------------------------------------------------
|
|
154
134
|
// Top-level contract
|
|
155
135
|
// ---------------------------------------------------------------------------
|
|
@@ -179,6 +159,6 @@ export interface SliceContract {
|
|
|
179
159
|
conflicts?: string[];
|
|
180
160
|
/** Map of previous-version → migration script id. */
|
|
181
161
|
migrationFrom?: Record<string, string>;
|
|
182
|
-
/**
|
|
183
|
-
|
|
162
|
+
/** Portability gate — feeds the forbidden-terms audit. */
|
|
163
|
+
generalization?: SliceGeneralization;
|
|
184
164
|
}
|
package/lib/contract-validate.ts
CHANGED
|
@@ -86,54 +86,45 @@ export function validateConvex(c: SliceContract): void {
|
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
export function
|
|
90
|
-
if (c.
|
|
91
|
-
if (!c.
|
|
92
|
-
throw new Error(`defineSliceContract(${c.id}): bidir must be an object`);
|
|
93
|
-
}
|
|
94
|
-
const policies = ["auto-pr", "notify", "manual", "frozen"];
|
|
95
|
-
if (!policies.includes(c.bidir.syncPolicy)) {
|
|
96
|
-
throw new Error(
|
|
97
|
-
`defineSliceContract(${c.id}): bidir.syncPolicy "${String(c.bidir.syncPolicy)}" must be one of ${policies.join("|")}`,
|
|
98
|
-
);
|
|
99
|
-
}
|
|
100
|
-
if (!c.bidir.generalization || typeof c.bidir.generalization !== "object") {
|
|
89
|
+
export function validateGeneralization(c: SliceContract): void {
|
|
90
|
+
if (c.generalization === undefined) return;
|
|
91
|
+
if (!c.generalization || typeof c.generalization !== "object") {
|
|
101
92
|
throw new Error(
|
|
102
|
-
`defineSliceContract(${c.id}):
|
|
93
|
+
`defineSliceContract(${c.id}): generalization must be an object`,
|
|
103
94
|
);
|
|
104
95
|
}
|
|
105
96
|
const levels = ["portable", "needs-adapter", "consumer-locked"];
|
|
106
|
-
if (!levels.includes(c.
|
|
97
|
+
if (!levels.includes(c.generalization.level)) {
|
|
107
98
|
throw new Error(
|
|
108
|
-
`defineSliceContract(${c.id}):
|
|
99
|
+
`defineSliceContract(${c.id}): generalization.level "${String(c.generalization.level)}" must be one of ${levels.join("|")}`,
|
|
109
100
|
);
|
|
110
101
|
}
|
|
111
|
-
const ft = c.
|
|
102
|
+
const ft = c.generalization.forbiddenTerms;
|
|
112
103
|
if (ft !== undefined) {
|
|
113
104
|
if (!Array.isArray(ft)) {
|
|
114
105
|
throw new Error(
|
|
115
|
-
`defineSliceContract(${c.id}):
|
|
106
|
+
`defineSliceContract(${c.id}): generalization.forbiddenTerms must be an array`,
|
|
116
107
|
);
|
|
117
108
|
}
|
|
118
109
|
for (const t of ft) {
|
|
119
110
|
if (typeof t !== "string" || t.length === 0) {
|
|
120
111
|
throw new Error(
|
|
121
|
-
`defineSliceContract(${c.id}):
|
|
112
|
+
`defineSliceContract(${c.id}): generalization.forbiddenTerms entries must be non-empty strings`,
|
|
122
113
|
);
|
|
123
114
|
}
|
|
124
115
|
}
|
|
125
116
|
}
|
|
126
|
-
const rp = c.
|
|
117
|
+
const rp = c.generalization.requiredProps;
|
|
127
118
|
if (rp !== undefined) {
|
|
128
119
|
if (!Array.isArray(rp)) {
|
|
129
120
|
throw new Error(
|
|
130
|
-
`defineSliceContract(${c.id}):
|
|
121
|
+
`defineSliceContract(${c.id}): generalization.requiredProps must be an array`,
|
|
131
122
|
);
|
|
132
123
|
}
|
|
133
124
|
for (const p of rp) {
|
|
134
125
|
if (typeof p !== "string" || p.length === 0) {
|
|
135
126
|
throw new Error(
|
|
136
|
-
`defineSliceContract(${c.id}):
|
|
127
|
+
`defineSliceContract(${c.id}): generalization.requiredProps entries must be non-empty strings`,
|
|
137
128
|
);
|
|
138
129
|
}
|
|
139
130
|
}
|
package/lib/contract.ts
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
validateHeader,
|
|
14
14
|
validateRbac,
|
|
15
15
|
validateConvex,
|
|
16
|
-
|
|
16
|
+
validateGeneralization,
|
|
17
17
|
validateConflicts,
|
|
18
18
|
} from "./contract-validate";
|
|
19
19
|
|
|
@@ -24,10 +24,8 @@ export type {
|
|
|
24
24
|
ConvexNamespace,
|
|
25
25
|
SliceContractRequires,
|
|
26
26
|
SliceContractProvides,
|
|
27
|
-
SliceSyncPolicy,
|
|
28
27
|
GeneralizationLevel,
|
|
29
28
|
SliceGeneralization,
|
|
30
|
-
SliceBidirContract,
|
|
31
29
|
SliceContract,
|
|
32
30
|
} from "./contract-types";
|
|
33
31
|
|
|
@@ -62,7 +60,7 @@ export function defineSliceContract(c: SliceContract): SliceContract {
|
|
|
62
60
|
validateHeader(c);
|
|
63
61
|
validateRbac(c);
|
|
64
62
|
validateConvex(c);
|
|
65
|
-
|
|
63
|
+
validateGeneralization(c);
|
|
66
64
|
validateConflicts(c);
|
|
67
65
|
return c;
|
|
68
66
|
}
|
package/lib/manifest.json
CHANGED
|
@@ -2084,12 +2084,12 @@
|
|
|
2084
2084
|
"slug": "rate-limit",
|
|
2085
2085
|
"title": "Rate Limit",
|
|
2086
2086
|
"category": "infra",
|
|
2087
|
-
"description": "Convex-backed per-key request counter. Atomic check-and-increment via `consume` mutation; expired rows pruned by `_pruneExpired` internalMutation wired to a 5-min cron. Replaces single-replica in-memory Map so multi-replica Next deployments share buckets.
|
|
2087
|
+
"description": "Convex-backed per-key request counter. Atomic check-and-increment via `consume` mutation; expired rows pruned by `_pruneExpired` internalMutation wired to a 5-min cron. Replaces single-replica in-memory Map so multi-replica Next deployments share buckets. Limits live in an in-code POLICY map keyed by namespace prefix (admin-login:<ip>, mcp:<ip>) — never caller-supplied; optional RATE_LIMIT_SERVER_KEY env gates anonymous calls. Lifted 2026-05-16 from rahmanef.com; hardened 2026-06-07.",
|
|
2088
2088
|
"source": "rahmanef.com",
|
|
2089
2089
|
"install": "npx rahman-resources add rate-limit",
|
|
2090
2090
|
"npmPackages": [],
|
|
2091
2091
|
"exampleCode": "",
|
|
2092
|
-
"agentRecipe": "Run `npx rr add rate-limit`. Compose `rateLimitTables` into root convex/schema.ts. Wire `internal.features.rate_limit.mutations._pruneExpired` into convex/crons.ts every 5 min.
|
|
2092
|
+
"agentRecipe": "Run `npx rr add rate-limit`. Compose `rateLimitTables` into root convex/schema.ts. Wire `internal.features.rate_limit.mutations._pruneExpired` into convex/crons.ts every 5 min. Add your namespace to the in-code POLICY map, then call `api.features.rate_limit.mutations.consume({ key, serverKey })` from server-side handlers — keep a fail-open wrapper so a Convex outage doesn't 503 the route. Set RATE_LIMIT_SERVER_KEY on the deployment to block anonymous consume calls.",
|
|
2093
2093
|
"tags": [
|
|
2094
2094
|
"infra",
|
|
2095
2095
|
"rate-limit",
|
|
@@ -2720,7 +2720,7 @@
|
|
|
2720
2720
|
"title": "Image Editor — layered raster editor",
|
|
2721
2721
|
"category": "os",
|
|
2722
2722
|
"kind": "ui",
|
|
2723
|
-
"version": "2.0.
|
|
2723
|
+
"version": "2.0.2",
|
|
2724
2724
|
"description": "A Photoshop-style raster image editor built on Konva. Layers panel (reorder, opacity, visibility, lock, 16 blend modes), free transform (move/scale/rotate/flip via a Transformer), image + text + shape + paint layers, brush & eraser with size/opacity/hardness, non-destructive adjustments + filters, canvas resize/aspect presets, and LAYER STYLES: stroke, drop shadow, outer glow, clipping mask. One-click BACKGROUND REMOVAL runs fully in-browser via @imgly/background-removal (free, no API key — downloads a small ONNX model on first use). Undo/redo, zoom/pan, shortcuts, PNG/JPG/WebP export. v2 adds an AI FUNCTION-CALLING layer: every editor operation is a named, schema'd command (EDITOR_COMMANDS registry + useEditorCommands binding) driven by an in-editor chat; the streaming bridge is injectable via configureAgentStream(fn) and everything except the chat works without it. A headless server barrel (server.ts) runs commands against documents with no DOM. Image I/O via props (initialImage / onSave).",
|
|
2725
2725
|
"source": "rahmanef63/os-vps",
|
|
2726
2726
|
"slicePath": "frontend/slices/image-editor",
|
|
@@ -2768,7 +2768,7 @@
|
|
|
2768
2768
|
"title": "Reel — video timeline editor",
|
|
2769
2769
|
"category": "os",
|
|
2770
2770
|
"kind": "ui",
|
|
2771
|
-
"version": "1.0.
|
|
2771
|
+
"version": "1.0.2",
|
|
2772
2772
|
"description": "A complete in-browser video editor. Real media clips (image/video/audio) on a layered multi-track timeline — the top row renders frontmost, with ▲▼ reorder and per-track lock/hide/mute. ONE Canvas-2D draw path is shared by the live preview and the realtime MediaRecorder exporter, so what you see is exactly what renders (WebM with real mixed audio: per-clip volume/fades/auto-duck through a streaming audio graph). Per-clip trim/speed (0.25–4×)/reverse, dissolve/wipe/slide transitions via clip overlap, keyframes (opacity/scale/x/y/rotation) with easing + one-click In/Out animation presets, text styling with preset grid, color grading + vignette, filmstrip thumbnails + real waveforms, snapping, split/duplicate. The workspace is config-driven: 6 resizable layout presets (react-resizable-panels v4) incl. quick-import files-pane layouts, plus custom composition size. Drafts auto-save to localStorage. Self-contained: toasts via sonner, the files pane runs on an injectable fs adapter (configureReelFs; in-memory mock by default), and shell hooks (inspector/activity) are inert seams in lib/host.ts.",
|
|
2773
2773
|
"source": "rahmanef63/os-vps",
|
|
2774
2774
|
"slicePath": "frontend/slices/reel-editor",
|
|
@@ -2846,7 +2846,7 @@
|
|
|
2846
2846
|
"title": "Code — overlay syntax editor",
|
|
2847
2847
|
"category": "os",
|
|
2848
2848
|
"kind": "ui",
|
|
2849
|
-
"version": "1.0.
|
|
2849
|
+
"version": "1.0.2",
|
|
2850
2850
|
"description": "A lightweight code editor in the VS-Code spirit without the weight: a transparent textarea layered over a highlighted pre (regex tokenizer for JS/TS/JSON/CSS) gives real editing with live syntax color and a line-number gutter; a tab strip tracks dirty buffers with Cmd/Ctrl+S save; a status bar shows path, Ln/Col, tab size, language and save state. The explorer is a lazy per-directory tree — each folder lists on expand, with inline new-file/new-folder affordances — rendered as a rail on desktop and a Sheet on mobile, and the new-file form is a responsive dialog ⇄ bottom drawer. The filesystem is INJECTED via a small CodeFsAdapter (list/read/write/mkdir): point configureCodeFs at a real API or use the bundled writable in-memory mock (seeded sample tree) so it works with zero backend. Writes are best-effort — a read-only host flags the save but keeps the local buffer. Pairs with file-explorer (onOpenFile → payload) and appshell.",
|
|
2851
2851
|
"source": "rahmanef63/os-vps",
|
|
2852
2852
|
"slicePath": "frontend/slices/code-editor",
|
|
@@ -2977,7 +2977,7 @@
|
|
|
2977
2977
|
"title": "Browser — remote headless-browser chrome",
|
|
2978
2978
|
"category": "os",
|
|
2979
2979
|
"kind": "ui",
|
|
2980
|
-
"version": "1.0.
|
|
2980
|
+
"version": "1.0.2",
|
|
2981
2981
|
"description": "Full browser chrome for a REMOTE headless browser: omnibar with search-or-URL detection, bookmark bar, history view (localStorage-persisted), favicons with globe fallback, busy states, and a screenshot viewport that forwards clicks/typing/keys/scroll into the remote page. The backend is INJECTED via a small BrowserAdapter (state/screenshot/act): point configureBrowser at a real headless-Chromium service (e.g. Playwright behind an authed route — any site renders, no X-Frame-Options problem) or keep the bundled offline canvas demo renderer that fakes the viewport so the whole chrome works with zero backend. Self-contained: shell inspector hooks are inert seams in lib/host.ts.",
|
|
2982
2982
|
"source": "rahmanef63/os-vps",
|
|
2983
2983
|
"slicePath": "frontend/slices/browser",
|
|
@@ -3083,7 +3083,7 @@
|
|
|
3083
3083
|
"title": "AppShell — Desktop + Mobile OS Shell",
|
|
3084
3084
|
"category": "os",
|
|
3085
3085
|
"kind": "full",
|
|
3086
|
-
"version": "1.
|
|
3086
|
+
"version": "1.3.1",
|
|
3087
3087
|
"description": "Generic, brand-free OS-style shell framework. One <AppShell manifest> wrapper provider gives a project a macOS-style window manager (drag/snap/maximize, dock, menu bar, Spotlight) AND an iOS-style mobile surface (home pager, app library, control center, widgets), driven entirely by a manifest: brand, apps, features, surface regions, capabilities, persistence, keymap. Five shell features (search, inspector, notifications, control-center, widgets) are bundled as defineFeature() contributions inside the slice and mount via named <Slot>s. Responsiveness is a single ResponsiveProvider + 4 DRY primitives (AppFrame, MasterDetail, ResponsiveToolbar, TouchList). Imports nothing project-specific — the consumer injects data/auth/AI through manifest.capabilities. Lifted from os-vps (Topside).",
|
|
3088
3088
|
"source": "rahmanef63/os-vps",
|
|
3089
3089
|
"slicePath": "frontend/slices/appshell",
|
|
@@ -4155,8 +4155,8 @@
|
|
|
4155
4155
|
"title": "Rate Limit",
|
|
4156
4156
|
"category": "infra",
|
|
4157
4157
|
"kind": "backend",
|
|
4158
|
-
"version": "0.
|
|
4159
|
-
"description": "Convex-backed per-key request counter. Atomic check-and-increment via `consume` mutation; expired rows pruned by `_pruneExpired` internalMutation wired to a 5-min cron. Replaces single-replica in-memory Map so multi-replica Next deployments share buckets.
|
|
4158
|
+
"version": "0.2.0",
|
|
4159
|
+
"description": "Convex-backed per-key request counter. Atomic check-and-increment via `consume` mutation; expired rows pruned by `_pruneExpired` internalMutation wired to a 5-min cron. Replaces single-replica in-memory Map so multi-replica Next deployments share buckets. Limits live in an in-code POLICY map keyed by namespace prefix (admin-login:<ip>, mcp:<ip>) — never caller-supplied; optional RATE_LIMIT_SERVER_KEY env gates anonymous calls. Lifted 2026-05-16 from rahmanef.com; hardened 2026-06-07.",
|
|
4160
4160
|
"source": "rahmanef.com",
|
|
4161
4161
|
"slicePath": "frontend/slices/rate-limit",
|
|
4162
4162
|
"convexPaths": [
|
|
@@ -4174,7 +4174,7 @@
|
|
|
4174
4174
|
"backend",
|
|
4175
4175
|
"throttle"
|
|
4176
4176
|
],
|
|
4177
|
-
"agentRecipe": "Run `npx rr add rate-limit`. Compose `rateLimitTables` into root convex/schema.ts. Wire `internal.features.rate_limit.mutations._pruneExpired` into convex/crons.ts every 5 min.
|
|
4177
|
+
"agentRecipe": "Run `npx rr add rate-limit`. Compose `rateLimitTables` into root convex/schema.ts. Wire `internal.features.rate_limit.mutations._pruneExpired` into convex/crons.ts every 5 min. Add your namespace to the in-code POLICY map, then call `api.features.rate_limit.mutations.consume({ key, serverKey })` from server-side handlers — keep a fail-open wrapper so a Convex outage doesn't 503 the route. Set RATE_LIMIT_SERVER_KEY on the deployment to block anonymous consume calls."
|
|
4178
4178
|
},
|
|
4179
4179
|
{
|
|
4180
4180
|
"slug": "testimonials",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rahman-resources",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.13.0",
|
|
4
4
|
"description": "Rahman Resources (rr) — shadcn-style installer for vertical slices. `npx resources add <slug>` copies slice into your project's `slices/<slug>/`. You own the files.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|