@agent-native/core 0.182.0 → 0.182.1-nightly-20260916202735
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/corpus/README.md +1 -1
- package/corpus/templates/analytics/.agents/skills/shadcn-ui/SKILL.md +65 -6
- package/corpus/templates/analytics/.mcp.json +8 -0
- package/corpus/templates/analytics/changelog/2026-09-16-dashboards-open-without-background-synchronization-scans-dur.md +6 -0
- package/corpus/templates/analytics/server/plugins/collab.ts +2 -0
- package/corpus/templates/assets/.agents/skills/shadcn-ui/SKILL.md +65 -6
- package/corpus/templates/assets/.mcp.json +8 -0
- package/corpus/templates/brain/.agents/skills/shadcn-ui/SKILL.md +65 -6
- package/corpus/templates/brain/.mcp.json +8 -0
- package/corpus/templates/calendar/.agents/skills/shadcn-ui/SKILL.md +65 -6
- package/corpus/templates/calendar/.mcp.json +8 -0
- package/corpus/templates/chat/.agents/skills/shadcn-ui/SKILL.md +65 -6
- package/corpus/templates/chat/.mcp.json +8 -0
- package/corpus/templates/clips/.agents/skills/shadcn-ui/SKILL.md +65 -6
- package/corpus/templates/clips/.mcp.json +8 -0
- package/corpus/templates/clips/desktop/.mcp.json +8 -0
- package/corpus/templates/content/.agents/skills/shadcn-ui/SKILL.md +65 -6
- package/corpus/templates/content/.mcp.json +8 -0
- package/corpus/templates/crm/.agents/skills/shadcn-ui/SKILL.md +65 -6
- package/corpus/templates/crm/.mcp.json +8 -0
- package/corpus/templates/design/.agents/skills/shadcn-ui/SKILL.md +65 -6
- package/corpus/templates/design/.mcp.json +8 -0
- package/corpus/templates/design/changelog/2026-09-16-design-files-avoid-cold-start-scans-and-open-faster-after-id.md +6 -0
- package/corpus/templates/dispatch/.agents/skills/shadcn-ui/SKILL.md +65 -6
- package/corpus/templates/dispatch/.mcp.json +8 -0
- package/corpus/templates/factory/.agents/skills/shadcn-ui/SKILL.md +65 -6
- package/corpus/templates/factory/.mcp.json +8 -0
- package/corpus/templates/forms/.agents/skills/shadcn-ui/SKILL.md +65 -6
- package/corpus/templates/forms/.mcp.json +8 -0
- package/corpus/templates/mail/.agents/skills/shadcn-ui/SKILL.md +65 -6
- package/corpus/templates/mail/.mcp.json +8 -0
- package/corpus/templates/plan/.agents/skills/shadcn-ui/SKILL.md +65 -6
- package/corpus/templates/plan/.mcp.json +8 -0
- package/corpus/templates/slides/.agents/skills/shadcn-ui/SKILL.md +65 -6
- package/corpus/templates/slides/.mcp.json +8 -0
- package/corpus/templates/slides/server/plugins/collab.ts +3 -0
- package/corpus/templates/tasks/.agents/skills/shadcn-ui/SKILL.md +65 -6
- package/corpus/templates/tasks/.mcp.json +8 -0
- package/dist/automation/index.d.ts +1 -1
- package/dist/cli/create.js +10 -5
- package/dist/collab/storage.d.ts +1 -3
- package/dist/collab/storage.js +2 -9
- package/dist/collab/ydoc-manager.d.ts +2 -2
- package/dist/collab/ydoc-manager.js +45 -11
- package/dist/deploy/build.js +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +8 -8
- package/dist/org/handlers.d.ts +1 -1
- package/dist/provider-api/actions/provider-api.d.ts +4 -4
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/server/collab-plugin.d.ts +18 -5
- package/dist/server/collab-plugin.js +173 -63
- package/dist/templates/chat/.agents/skills/shadcn-ui/SKILL.md +65 -6
- package/dist/templates/chat/.mcp.json +8 -0
- package/dist/templates/default/.agents/skills/shadcn-ui/SKILL.md +65 -6
- package/dist/templates/default/.mcp.json +8 -0
- package/dist/templates/workspace-core/.agents/skills/shadcn-ui/SKILL.md +65 -6
- package/dist/templates/workspace-root/package.json +1 -1
- package/dist/templates/workspace-root/pnpm-workspace.yaml +18 -0
- package/package.json +4 -4
- package/src/templates/chat/.agents/skills/shadcn-ui/SKILL.md +65 -6
- package/src/templates/chat/.mcp.json +8 -0
- package/src/templates/default/.agents/skills/shadcn-ui/SKILL.md +65 -6
- package/src/templates/default/.mcp.json +8 -0
- package/src/templates/workspace-core/.agents/skills/shadcn-ui/SKILL.md +65 -6
- package/src/templates/workspace-root/package.json +1 -1
- package/src/templates/workspace-root/pnpm-workspace.yaml +18 -0
package/dist/deploy/build.js
CHANGED
|
@@ -1967,7 +1967,7 @@ function patchReactRouterServerManifestSource(source, serverBuildFile, clientMan
|
|
|
1967
1967
|
const replacement = JSON.stringify(mergeReactRouterServerManifest(serverManifest, clientManifest));
|
|
1968
1968
|
return (source.slice(0, valueStart) + replacement + source.slice(assignmentEnd));
|
|
1969
1969
|
}
|
|
1970
|
-
const assignments =
|
|
1970
|
+
const assignments = /(?:\b[A-Za-z_][\w$]*|\$[\w$]*)\s*=\s*(\{\s*(?:entry|["']entry["'])\s*:)/g;
|
|
1971
1971
|
for (const match of source.matchAll(assignments)) {
|
|
1972
1972
|
const valueStart = match.index + match[0].lastIndexOf("{");
|
|
1973
1973
|
const valueEnd = findJavaScriptObjectEnd(source, valueStart);
|
|
@@ -17,11 +17,11 @@ declare const _default: import("../../action.js").ActionDefinition<{
|
|
|
17
17
|
id?: undefined;
|
|
18
18
|
provider?: undefined;
|
|
19
19
|
} | {
|
|
20
|
-
error?: undefined;
|
|
21
20
|
configured?: undefined;
|
|
22
21
|
connectPath?: undefined;
|
|
23
22
|
url: string;
|
|
24
23
|
id: string;
|
|
25
24
|
provider: string;
|
|
25
|
+
error?: undefined;
|
|
26
26
|
}>;
|
|
27
27
|
export default _default;
|
|
@@ -41,27 +41,27 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
|
|
|
41
41
|
thumbsUpRate: number;
|
|
42
42
|
avgEvalScore: number;
|
|
43
43
|
} | {
|
|
44
|
-
|
|
44
|
+
error?: undefined;
|
|
45
|
+
ok?: undefined;
|
|
45
46
|
summary: import("./types.js").TraceSummary;
|
|
46
47
|
spans: import("./types.js").TraceSpan[];
|
|
48
|
+
id?: undefined;
|
|
49
|
+
} | {
|
|
47
50
|
error?: undefined;
|
|
48
51
|
ok?: undefined;
|
|
49
|
-
} | {
|
|
50
52
|
summary?: undefined;
|
|
51
53
|
spans?: undefined;
|
|
52
54
|
id: string;
|
|
53
|
-
error?: undefined;
|
|
54
|
-
ok?: undefined;
|
|
55
55
|
} | {
|
|
56
|
-
|
|
56
|
+
ok?: undefined;
|
|
57
57
|
summary?: undefined;
|
|
58
58
|
spans?: undefined;
|
|
59
|
+
id?: undefined;
|
|
59
60
|
error: any;
|
|
60
|
-
ok?: undefined;
|
|
61
61
|
} | {
|
|
62
|
-
|
|
62
|
+
error?: undefined;
|
|
63
63
|
summary?: undefined;
|
|
64
64
|
spans?: undefined;
|
|
65
|
+
id?: undefined;
|
|
65
66
|
ok: boolean;
|
|
66
|
-
error?: undefined;
|
|
67
67
|
}>>;
|
package/dist/org/handlers.d.ts
CHANGED
|
@@ -52,9 +52,9 @@ export declare const setWorkspaceAppDefaultVisibilityHandler: import("h3").Event
|
|
|
52
52
|
}>>;
|
|
53
53
|
/** POST /_agent-native/org — create a new organization */
|
|
54
54
|
export declare const createOrgHandler: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
55
|
+
name?: undefined;
|
|
55
56
|
success: boolean;
|
|
56
57
|
id?: undefined;
|
|
57
|
-
name?: undefined;
|
|
58
58
|
role?: undefined;
|
|
59
59
|
} | {
|
|
60
60
|
success?: undefined;
|
|
@@ -311,7 +311,6 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
|
|
|
311
311
|
notes?: string | undefined;
|
|
312
312
|
scope?: "org" | "user" | undefined;
|
|
313
313
|
}, {
|
|
314
|
-
message?: undefined;
|
|
315
314
|
id?: undefined;
|
|
316
315
|
provider?: undefined;
|
|
317
316
|
deleted?: undefined;
|
|
@@ -328,8 +327,8 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
|
|
|
328
327
|
count: number;
|
|
329
328
|
registered?: undefined;
|
|
330
329
|
label?: undefined;
|
|
331
|
-
} | {
|
|
332
330
|
message?: undefined;
|
|
331
|
+
} | {
|
|
333
332
|
id?: undefined;
|
|
334
333
|
deleted?: undefined;
|
|
335
334
|
providers?: undefined;
|
|
@@ -338,8 +337,8 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
|
|
|
338
337
|
provider: import("../custom-registry.js").CustomProviderConfig;
|
|
339
338
|
registered?: undefined;
|
|
340
339
|
label?: undefined;
|
|
341
|
-
} | {
|
|
342
340
|
message?: undefined;
|
|
341
|
+
} | {
|
|
343
342
|
provider?: undefined;
|
|
344
343
|
deleted?: undefined;
|
|
345
344
|
providers?: undefined;
|
|
@@ -348,8 +347,8 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
|
|
|
348
347
|
id: string;
|
|
349
348
|
registered?: undefined;
|
|
350
349
|
label?: undefined;
|
|
351
|
-
} | {
|
|
352
350
|
message?: undefined;
|
|
351
|
+
} | {
|
|
353
352
|
provider?: undefined;
|
|
354
353
|
found?: undefined;
|
|
355
354
|
providers?: undefined;
|
|
@@ -358,6 +357,7 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
|
|
|
358
357
|
id: string;
|
|
359
358
|
registered?: undefined;
|
|
360
359
|
label?: undefined;
|
|
360
|
+
message?: undefined;
|
|
361
361
|
} | {
|
|
362
362
|
provider?: undefined;
|
|
363
363
|
deleted?: undefined;
|
|
@@ -48,8 +48,8 @@ export declare function handleUpdateResource(event: any): Promise<import("./stor
|
|
|
48
48
|
}>;
|
|
49
49
|
/** DELETE /_agent-native/resources/:id — delete a resource */
|
|
50
50
|
export declare function handleDeleteResource(event: any): Promise<{
|
|
51
|
-
error: string;
|
|
52
51
|
ok?: undefined;
|
|
52
|
+
error: string;
|
|
53
53
|
} | {
|
|
54
54
|
error?: undefined;
|
|
55
55
|
ok: boolean;
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* });
|
|
13
13
|
* ```
|
|
14
14
|
*/
|
|
15
|
+
import { type DbExec } from "../db/client.js";
|
|
15
16
|
type NitroPluginDef = (nitroApp: any) => void | Promise<void>;
|
|
16
17
|
export type CollabResourceIdResolver = (docId: string) => string | null | Promise<string | null>;
|
|
17
18
|
export type CollabAccess = {
|
|
@@ -39,10 +40,16 @@ export interface CollabPluginOptions {
|
|
|
39
40
|
contentColumn?: string;
|
|
40
41
|
/** Column name for the document ID. Default: "id" */
|
|
41
42
|
idColumn?: string;
|
|
42
|
-
/** Whether to
|
|
43
|
+
/** Whether to lazily seed a source row on its first collab request. Default: true */
|
|
43
44
|
autoSeed?: boolean;
|
|
44
45
|
/** Map a source-table id to the id used by the collab document store. */
|
|
45
46
|
resolveCollabDocumentId?: (sourceId: string) => string;
|
|
47
|
+
/**
|
|
48
|
+
* Map a collab document id back to the source-table id for lazy seeding.
|
|
49
|
+
* Without this, the legacy forward resolver is supported with a first-load
|
|
50
|
+
* compatibility scan because arbitrary functions are not invertible.
|
|
51
|
+
*/
|
|
52
|
+
resolveSourceIdFromCollabDocumentId?: (docId: string) => string;
|
|
46
53
|
/**
|
|
47
54
|
* Callback invoked after a collab update to sync the content column.
|
|
48
55
|
* If not provided, the plugin auto-syncs using table/contentColumn/idColumn.
|
|
@@ -79,9 +86,15 @@ export interface CollabPluginOptions {
|
|
|
79
86
|
maxPayloadBytes?: number;
|
|
80
87
|
}
|
|
81
88
|
export declare function normalizeCollabAccess(options: Pick<CollabPluginOptions, "access" | "resourceType" | "resolveResourceId">): NormalizedCollabAccess;
|
|
89
|
+
/**
|
|
90
|
+
* Coalesce concurrent first loads for one document. The optional lock lets a
|
|
91
|
+
* database-backed caller extend that guarantee across server instances.
|
|
92
|
+
*/
|
|
93
|
+
export declare function createCollabSourceSeeder(options: {
|
|
94
|
+
hasState: (docId: string) => Promise<boolean>;
|
|
95
|
+
loadSource: (docId: string) => Promise<string | null>;
|
|
96
|
+
seed: (docId: string, source: string, client?: DbExec) => Promise<void>;
|
|
97
|
+
withLock?: (docId: string, run: (client?: DbExec) => Promise<void>) => Promise<void>;
|
|
98
|
+
}): (docId: string) => Promise<void>;
|
|
82
99
|
export declare function createCollabPlugin(options?: CollabPluginOptions): NitroPluginDef;
|
|
83
|
-
export declare function selectUnseededCollabRows(rows: ReadonlyArray<Record<string, unknown>>, idColumn: string, existingDocIds: ReadonlySet<string>, resolveCollabDocumentId?: (sourceId: string) => string): Array<{
|
|
84
|
-
row: Record<string, unknown>;
|
|
85
|
-
docId: string;
|
|
86
|
-
}>;
|
|
87
100
|
export {};
|
|
@@ -16,10 +16,10 @@ import { defineEventHandler, getMethod, setResponseStatus, } from "h3";
|
|
|
16
16
|
import { postAwareness, getActiveUsers } from "../collab/awareness.js";
|
|
17
17
|
import { getCollabEmitter } from "../collab/emitter.js";
|
|
18
18
|
import { getCollabState, postCollabUpdate, postCollabText, postCollabSearchReplace, } from "../collab/routes.js";
|
|
19
|
-
import {
|
|
19
|
+
import { hasCollabState } from "../collab/storage.js";
|
|
20
20
|
import { postCollabJson, getCollabJson, postCollabPatch, } from "../collab/struct-routes.js";
|
|
21
21
|
import { seedFromText, seedFromJson } from "../collab/ydoc-manager.js";
|
|
22
|
-
import { getDbExec } from "../db/client.js";
|
|
22
|
+
import { getDbExec, withDbExec } from "../db/client.js";
|
|
23
23
|
import { getOrgContext } from "../org/context.js";
|
|
24
24
|
import { resolveAccess, assertAccess } from "../sharing/access.js";
|
|
25
25
|
import { getSession } from "./auth.js";
|
|
@@ -79,10 +79,60 @@ function warnForImplicitAllAuthenticatedAccess(table) {
|
|
|
79
79
|
'Use access: { mode: "resource", resourceType: "..." } for access-scoped delivery, ' +
|
|
80
80
|
'or access: { mode: "all-authenticated" } to explicitly acknowledge deployment-wide delivery.');
|
|
81
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* Coalesce concurrent first loads for one document. The optional lock lets a
|
|
84
|
+
* database-backed caller extend that guarantee across server instances.
|
|
85
|
+
*/
|
|
86
|
+
export function createCollabSourceSeeder(options) {
|
|
87
|
+
const inFlight = new Map();
|
|
88
|
+
return async (docId) => {
|
|
89
|
+
const existing = inFlight.get(docId);
|
|
90
|
+
if (existing)
|
|
91
|
+
return existing;
|
|
92
|
+
const seed = async (client) => {
|
|
93
|
+
if (await options.hasState(docId))
|
|
94
|
+
return;
|
|
95
|
+
const source = await options.loadSource(docId);
|
|
96
|
+
if (source === null)
|
|
97
|
+
return;
|
|
98
|
+
if (client) {
|
|
99
|
+
await options.seed(docId, source, client);
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
await options.seed(docId, source);
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
const pending = (async () => {
|
|
106
|
+
// Keep the normal seeded-document path out of a transaction. The second
|
|
107
|
+
// check inside the lock closes the cross-instance race for cold docs.
|
|
108
|
+
if (await options.hasState(docId))
|
|
109
|
+
return;
|
|
110
|
+
if (options.withLock) {
|
|
111
|
+
await options.withLock(docId, seed);
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
await seed();
|
|
115
|
+
}
|
|
116
|
+
})();
|
|
117
|
+
inFlight.set(docId, pending);
|
|
118
|
+
try {
|
|
119
|
+
await pending;
|
|
120
|
+
}
|
|
121
|
+
finally {
|
|
122
|
+
if (inFlight.get(docId) === pending)
|
|
123
|
+
inFlight.delete(docId);
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
}
|
|
82
127
|
export function createCollabPlugin(options = {}) {
|
|
83
128
|
const normalizedAccess = normalizeCollabAccess(options);
|
|
84
129
|
const { table = "documents", contentColumn = "content", idColumn = "id", autoSeed = true, maxPayloadBytes = DEFAULT_MAX_PAYLOAD_BYTES, } = options;
|
|
85
|
-
const
|
|
130
|
+
const resolveSourceIdFromCollabDocumentId = options.resolveSourceIdFromCollabDocumentId ?? ((docId) => docId);
|
|
131
|
+
const isJson = options.contentType === "json";
|
|
132
|
+
const seedColumn = isJson
|
|
133
|
+
? options.jsonColumn || contentColumn
|
|
134
|
+
: contentColumn;
|
|
135
|
+
const legacyResolveCollabDocumentId = options.resolveCollabDocumentId;
|
|
86
136
|
const resourceType = normalizedAccess.mode === "resource"
|
|
87
137
|
? normalizedAccess.resourceType
|
|
88
138
|
: undefined;
|
|
@@ -93,6 +143,115 @@ export function createCollabPlugin(options = {}) {
|
|
|
93
143
|
!normalizedAccess.explicit) {
|
|
94
144
|
warnForImplicitAllAuthenticatedAccess(table);
|
|
95
145
|
}
|
|
146
|
+
const ensureDocumentSeeded = autoSeed
|
|
147
|
+
? createCollabSourceSeeder({
|
|
148
|
+
hasState: hasCollabState,
|
|
149
|
+
loadSource: async (docId) => {
|
|
150
|
+
const readSource = (row, sourceId) => {
|
|
151
|
+
const source = row[seedColumn];
|
|
152
|
+
if (typeof source !== "string") {
|
|
153
|
+
throw new Error(`[collab] ${table}.${seedColumn} for ${sourceId} is unreadable`);
|
|
154
|
+
}
|
|
155
|
+
return source;
|
|
156
|
+
};
|
|
157
|
+
if (legacyResolveCollabDocumentId &&
|
|
158
|
+
!options.resolveSourceIdFromCollabDocumentId) {
|
|
159
|
+
// An arbitrary forward resolver cannot be inverted. Preserve
|
|
160
|
+
// existing consumers with a request-lazy compatibility scan; new
|
|
161
|
+
// configs should provide the reverse resolver to use the indexed
|
|
162
|
+
// source-id lookup.
|
|
163
|
+
const { rows } = await getDbExec().execute({
|
|
164
|
+
sql: `SELECT ${idColumn}, ${seedColumn} FROM ${table}`,
|
|
165
|
+
});
|
|
166
|
+
for (const row of rows) {
|
|
167
|
+
const rawSourceId = row[idColumn];
|
|
168
|
+
if (rawSourceId === null ||
|
|
169
|
+
rawSourceId === undefined ||
|
|
170
|
+
(typeof rawSourceId !== "string" &&
|
|
171
|
+
typeof rawSourceId !== "number" &&
|
|
172
|
+
typeof rawSourceId !== "bigint")) {
|
|
173
|
+
throw new Error(`[collab] ${table}.${idColumn} for a legacy lazy seed is unreadable`);
|
|
174
|
+
}
|
|
175
|
+
const sourceId = String(rawSourceId);
|
|
176
|
+
if (!sourceId) {
|
|
177
|
+
throw new Error(`[collab] ${table}.${idColumn} for a legacy lazy seed is unreadable`);
|
|
178
|
+
}
|
|
179
|
+
if (legacyResolveCollabDocumentId(sourceId) !== docId)
|
|
180
|
+
continue;
|
|
181
|
+
return readSource(row, sourceId);
|
|
182
|
+
}
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
const sourceId = resolveSourceIdFromCollabDocumentId(docId);
|
|
186
|
+
const { rows } = await getDbExec().execute({
|
|
187
|
+
sql: `SELECT ${seedColumn} FROM ${table} WHERE ${idColumn} = ?`,
|
|
188
|
+
args: [sourceId],
|
|
189
|
+
});
|
|
190
|
+
if (rows.length === 0)
|
|
191
|
+
return null;
|
|
192
|
+
return readSource(rows[0], sourceId);
|
|
193
|
+
},
|
|
194
|
+
seed: async (docId, source, client) => {
|
|
195
|
+
if (!isJson) {
|
|
196
|
+
if (client) {
|
|
197
|
+
await seedFromText(docId, source, "content", client);
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
await seedFromText(docId, source);
|
|
201
|
+
}
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
let parsed;
|
|
205
|
+
try {
|
|
206
|
+
parsed = JSON.parse(source);
|
|
207
|
+
}
|
|
208
|
+
catch (error) {
|
|
209
|
+
throw new Error(`[collab] ${table}.${seedColumn} for ${docId} contains invalid JSON`, { cause: error });
|
|
210
|
+
}
|
|
211
|
+
const type = Array.isArray(parsed) ? "array" : "map";
|
|
212
|
+
if (client) {
|
|
213
|
+
await seedFromJson(docId, parsed, "data", type, client);
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
await seedFromJson(docId, parsed, "data", type);
|
|
217
|
+
}
|
|
218
|
+
},
|
|
219
|
+
withLock: async (docId, run) => {
|
|
220
|
+
const client = getDbExec();
|
|
221
|
+
if (typeof client.transaction !== "function")
|
|
222
|
+
return run();
|
|
223
|
+
for (let attempt = 0; attempt < 20; attempt += 1) {
|
|
224
|
+
const acquired = await client.transaction(async (tx) => {
|
|
225
|
+
const { rows } = await tx.execute({
|
|
226
|
+
sql: "SELECT pg_try_advisory_xact_lock(hashtextextended(?, 0)) AS acquired",
|
|
227
|
+
args: [`${table}:${docId}`],
|
|
228
|
+
timeoutMs: 1_000,
|
|
229
|
+
});
|
|
230
|
+
const result = rows[0]?.acquired;
|
|
231
|
+
const acquired = result === true || result === "t"
|
|
232
|
+
? true
|
|
233
|
+
: result === false || result === "f"
|
|
234
|
+
? false
|
|
235
|
+
: null;
|
|
236
|
+
if (acquired === null) {
|
|
237
|
+
throw new Error(`[collab] advisory lock result for ${docId} is unreadable`);
|
|
238
|
+
}
|
|
239
|
+
if (!acquired)
|
|
240
|
+
return false;
|
|
241
|
+
await withDbExec(tx, () => run(tx));
|
|
242
|
+
return true;
|
|
243
|
+
});
|
|
244
|
+
if (acquired)
|
|
245
|
+
return;
|
|
246
|
+
// Do not leave every cold instance blocked on a database session
|
|
247
|
+
// while the first seed runs. This bounded retry keeps the wait in
|
|
248
|
+
// application memory and fails loudly if a seed cannot finish.
|
|
249
|
+
await new Promise((resolve) => setTimeout(resolve, Math.min(200, 25 * (attempt + 1))));
|
|
250
|
+
}
|
|
251
|
+
throw new Error(`[collab] timed out waiting to seed ${docId}; retry the request`);
|
|
252
|
+
},
|
|
253
|
+
})
|
|
254
|
+
: async () => { };
|
|
96
255
|
return async (nitroApp) => {
|
|
97
256
|
await awaitBootstrap(nitroApp);
|
|
98
257
|
const P = FRAMEWORK_ROUTE_PREFIX;
|
|
@@ -278,6 +437,14 @@ export function createCollabPlugin(options = {}) {
|
|
|
278
437
|
event.context._collabMaxPayloadBytes = maxPayloadBytes;
|
|
279
438
|
}
|
|
280
439
|
}
|
|
440
|
+
const needsSeed = (action === "state" && method === "GET") ||
|
|
441
|
+
(action === "update" && method === "POST") ||
|
|
442
|
+
(action === "text" && method === "POST") ||
|
|
443
|
+
(action === "search-replace" && method === "POST") ||
|
|
444
|
+
(action === "json" && (method === "GET" || method === "POST")) ||
|
|
445
|
+
(action === "patch" && method === "POST");
|
|
446
|
+
if (needsSeed)
|
|
447
|
+
await ensureDocumentSeeded(docId);
|
|
281
448
|
if (action === "state" && method === "GET")
|
|
282
449
|
return getCollabState(event);
|
|
283
450
|
if (action === "update" && method === "POST")
|
|
@@ -300,65 +467,8 @@ export function createCollabPlugin(options = {}) {
|
|
|
300
467
|
return { error: "Not found" };
|
|
301
468
|
});
|
|
302
469
|
}));
|
|
303
|
-
//
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
const seedColumn = isJson
|
|
307
|
-
? options.jsonColumn || contentColumn
|
|
308
|
-
: contentColumn;
|
|
309
|
-
// Run in background so it doesn't block startup
|
|
310
|
-
setTimeout(async () => {
|
|
311
|
-
try {
|
|
312
|
-
// Read existing ids once, then apply the plugin's document-id
|
|
313
|
-
// mapping before filtering. A source-table SQL predicate can use
|
|
314
|
-
// the wrong id for integrations such as Analytics' dash-* docs.
|
|
315
|
-
const existingDocIds = await listCollabDocIds();
|
|
316
|
-
const client = getDbExec();
|
|
317
|
-
const { rows } = await client.execute(`SELECT ${idColumn}, ${seedColumn} FROM ${table}`);
|
|
318
|
-
for (const { row, docId } of selectUnseededCollabRows(rows, idColumn, existingDocIds, resolveCollabDocumentId)) {
|
|
319
|
-
let seeded = false;
|
|
320
|
-
if (isJson) {
|
|
321
|
-
const raw = row[seedColumn] ?? "{}";
|
|
322
|
-
try {
|
|
323
|
-
const parsed = JSON.parse(raw);
|
|
324
|
-
const inferredType = Array.isArray(parsed)
|
|
325
|
-
? "array"
|
|
326
|
-
: "map";
|
|
327
|
-
await seedFromJson(docId, parsed, "data", inferredType);
|
|
328
|
-
seeded = true;
|
|
329
|
-
}
|
|
330
|
-
catch {
|
|
331
|
-
// Invalid JSON — skip
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
else {
|
|
335
|
-
const content = row[seedColumn] ?? "";
|
|
336
|
-
await seedFromText(docId, content);
|
|
337
|
-
seeded = true;
|
|
338
|
-
}
|
|
339
|
-
if (seeded)
|
|
340
|
-
existingDocIds.add(docId);
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
catch {
|
|
344
|
-
// Table may not exist yet on first boot — that's fine
|
|
345
|
-
}
|
|
346
|
-
}, 1000);
|
|
347
|
-
}
|
|
470
|
+
// Source rows are seeded lazily by the request path above. A cold-start
|
|
471
|
+
// scan here stampedes serverless instances and does work for documents no
|
|
472
|
+
// caller will ever open.
|
|
348
473
|
};
|
|
349
474
|
}
|
|
350
|
-
export function selectUnseededCollabRows(rows, idColumn, existingDocIds, resolveCollabDocumentId = (sourceId) => sourceId) {
|
|
351
|
-
const seenDocIds = new Set(existingDocIds);
|
|
352
|
-
const unseeded = [];
|
|
353
|
-
for (const row of rows) {
|
|
354
|
-
const sourceId = String(row[idColumn] ?? "");
|
|
355
|
-
if (!sourceId)
|
|
356
|
-
continue;
|
|
357
|
-
const docId = resolveCollabDocumentId(sourceId);
|
|
358
|
-
if (seenDocIds.has(docId))
|
|
359
|
-
continue;
|
|
360
|
-
seenDocIds.add(docId);
|
|
361
|
-
unseeded.push({ row, docId });
|
|
362
|
-
}
|
|
363
|
-
return unseeded;
|
|
364
|
-
}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: shadcn-ui
|
|
3
3
|
description: >-
|
|
4
|
-
|
|
5
|
-
when adding
|
|
6
|
-
|
|
7
|
-
file that happens to import a shadcn component.
|
|
4
|
+
Project-aware shadcn/ui components, registry MCP, theming, and design-system
|
|
5
|
+
lint feedback. Use when adding or changing React/Tailwind UI, a primitive,
|
|
6
|
+
theme, registry, or component contract in an Agent-Native app.
|
|
8
7
|
scope: dev
|
|
9
8
|
source: https://ui.shadcn.com/docs/skills
|
|
10
9
|
local-changes: >-
|
|
@@ -16,11 +15,18 @@ metadata:
|
|
|
16
15
|
|
|
17
16
|
# shadcn/ui
|
|
18
17
|
|
|
19
|
-
This skill keeps shadcn/ui work project-aware. Components are source files in the
|
|
18
|
+
This skill keeps shadcn/ui work project-aware. Components are source files in the
|
|
19
|
+
app, so always inspect the local project before adding, importing, or rewriting
|
|
20
|
+
them. When `@shadcn/lint` is configured, treat its feedback as implementation
|
|
21
|
+
guidance, not cosmetic noise. The framework repository runs it through the root
|
|
22
|
+
Oxlint configuration; generated workspaces carry this skill and the MCP
|
|
23
|
+
workflow, but do not automatically inherit that repository-only lint setup.
|
|
20
24
|
|
|
21
25
|
## First Steps
|
|
22
26
|
|
|
23
|
-
1. Work from the app root that owns `components.json`.
|
|
27
|
+
1. Work from the app root that owns `components.json`. In this monorepo, that is
|
|
28
|
+
the selected app/template directory (usually under `templates/`), not the
|
|
29
|
+
repository root.
|
|
24
30
|
2. In an Agent-Native app, inspect `app/design-system.ts` and
|
|
25
31
|
`ToolkitProvider` before choosing a primitive. A registered company design
|
|
26
32
|
system takes precedence over the default shadcn adapter.
|
|
@@ -29,6 +35,59 @@ This skill keeps shadcn/ui work project-aware. Components are source files in th
|
|
|
29
35
|
5. Check `app/components/ui/` or the resolved `ui` path before importing a component.
|
|
30
36
|
6. For unfamiliar components, run `pnpm dlx shadcn@latest docs <component>` and read the returned docs or examples before coding.
|
|
31
37
|
|
|
38
|
+
## MCP Registry Workflow
|
|
39
|
+
|
|
40
|
+
Each first-party app keeps the official shadcn MCP configuration in `.mcp.json`
|
|
41
|
+
next to its `components.json`. Start the agent from that app root so the MCP
|
|
42
|
+
server resolves the correct aliases, local UI directory, and Tailwind theme.
|
|
43
|
+
Do not add a single repository-root server: the root is a workspace, not a
|
|
44
|
+
shadcn project, and would make installs target the wrong project.
|
|
45
|
+
|
|
46
|
+
- Use the shadcn MCP to search, inspect, and install registry items when it is
|
|
47
|
+
connected. Prefer the app's configured registries over copied source.
|
|
48
|
+
- Use `pnpm dlx shadcn@latest info --json` for project context; the MCP has no
|
|
49
|
+
equivalent project-info tool.
|
|
50
|
+
- If the MCP is unavailable, use the equivalent `pnpm dlx shadcn@latest`
|
|
51
|
+
command from the same app root and continue following this skill.
|
|
52
|
+
- Keep registry URLs and aliases in that app's `components.json`. Never put
|
|
53
|
+
registry credentials in MCP config or checked-in source.
|
|
54
|
+
|
|
55
|
+
## Design-System Lint Feedback
|
|
56
|
+
|
|
57
|
+
In the framework repository, `pnpm lint` runs Oxlint with `@shadcn/lint` for the
|
|
58
|
+
scoped template UI paths. Run it after UI changes and fix every `shadcn/*`
|
|
59
|
+
finding before handing off. Template packages use the workspace lint
|
|
60
|
+
configuration; do not create a second local lint configuration just for one
|
|
61
|
+
app.
|
|
62
|
+
|
|
63
|
+
Before claiming that shadcn lint ran in another workspace, inspect its root
|
|
64
|
+
`package.json` and `.oxlintrc.json`. The generic generated workspace currently
|
|
65
|
+
exposes `pnpm lint` as formatting-only and does not ship `@shadcn/lint` or the
|
|
66
|
+
framework repository's Oxlint config. In that scaffold, use this skill plus the
|
|
67
|
+
connected shadcn MCP (or the CLI fallback) for design-system guidance, and do
|
|
68
|
+
not report `shadcn/*` checks as having run. If the workspace owner adopts the
|
|
69
|
+
linter, add the dependency, scoped config, and lint script together.
|
|
70
|
+
|
|
71
|
+
The Plan template is intentionally excluded from this rollout. Its files remain
|
|
72
|
+
under `templates/plan/**`, but the root Oxlint ignore list keeps them out of the
|
|
73
|
+
shadcn checks until that template has a separate lint baseline.
|
|
74
|
+
|
|
75
|
+
- `no-restyle`: use the component's `variant`, `size`, and semantic props; put
|
|
76
|
+
layout on a parent and add a component variant only when the design system
|
|
77
|
+
genuinely needs a new treatment.
|
|
78
|
+
- `no-raw-colors`: use the theme tokens in the app's `global.css`, not raw
|
|
79
|
+
Tailwind palette classes or hard-coded color attributes.
|
|
80
|
+
- `no-arbitrary-values`: use the theme scale or a declared token instead of
|
|
81
|
+
one-off bracket values.
|
|
82
|
+
- `no-inline-styles`: use classes or CSS custom properties for dynamic layout;
|
|
83
|
+
keep intentional editor/export exceptions scoped to their owning subsystem.
|
|
84
|
+
- `no-unknown-classes` and `require-static-classes`: use classes Tailwind can
|
|
85
|
+
generate and keep class strings statically discoverable.
|
|
86
|
+
|
|
87
|
+
The local primitive source is the contract boundary. Product code should consume
|
|
88
|
+
its props and variants; update `packages/toolkit/src/ui/` or an app's
|
|
89
|
+
`components/ui/` only when the design-system contract itself changes.
|
|
90
|
+
|
|
32
91
|
## Agent-Native Adapter Rule
|
|
33
92
|
|
|
34
93
|
Pages, routes, and domain components import controls through the app's local UI
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: shadcn-ui
|
|
3
3
|
description: >-
|
|
4
|
-
|
|
5
|
-
when adding
|
|
6
|
-
|
|
7
|
-
file that happens to import a shadcn component.
|
|
4
|
+
Project-aware shadcn/ui components, registry MCP, theming, and design-system
|
|
5
|
+
lint feedback. Use when adding or changing React/Tailwind UI, a primitive,
|
|
6
|
+
theme, registry, or component contract in an Agent-Native app.
|
|
8
7
|
scope: dev
|
|
9
8
|
source: https://ui.shadcn.com/docs/skills
|
|
10
9
|
local-changes: >-
|
|
@@ -16,11 +15,18 @@ metadata:
|
|
|
16
15
|
|
|
17
16
|
# shadcn/ui
|
|
18
17
|
|
|
19
|
-
This skill keeps shadcn/ui work project-aware. Components are source files in the
|
|
18
|
+
This skill keeps shadcn/ui work project-aware. Components are source files in the
|
|
19
|
+
app, so always inspect the local project before adding, importing, or rewriting
|
|
20
|
+
them. When `@shadcn/lint` is configured, treat its feedback as implementation
|
|
21
|
+
guidance, not cosmetic noise. The framework repository runs it through the root
|
|
22
|
+
Oxlint configuration; generated workspaces carry this skill and the MCP
|
|
23
|
+
workflow, but do not automatically inherit that repository-only lint setup.
|
|
20
24
|
|
|
21
25
|
## First Steps
|
|
22
26
|
|
|
23
|
-
1. Work from the app root that owns `components.json`.
|
|
27
|
+
1. Work from the app root that owns `components.json`. In this monorepo, that is
|
|
28
|
+
the selected app/template directory (usually under `templates/`), not the
|
|
29
|
+
repository root.
|
|
24
30
|
2. In an Agent-Native app, inspect `app/design-system.ts` and
|
|
25
31
|
`ToolkitProvider` before choosing a primitive. A registered company design
|
|
26
32
|
system takes precedence over the default shadcn adapter.
|
|
@@ -29,6 +35,59 @@ This skill keeps shadcn/ui work project-aware. Components are source files in th
|
|
|
29
35
|
5. Check `app/components/ui/` or the resolved `ui` path before importing a component.
|
|
30
36
|
6. For unfamiliar components, run `pnpm dlx shadcn@latest docs <component>` and read the returned docs or examples before coding.
|
|
31
37
|
|
|
38
|
+
## MCP Registry Workflow
|
|
39
|
+
|
|
40
|
+
Each first-party app keeps the official shadcn MCP configuration in `.mcp.json`
|
|
41
|
+
next to its `components.json`. Start the agent from that app root so the MCP
|
|
42
|
+
server resolves the correct aliases, local UI directory, and Tailwind theme.
|
|
43
|
+
Do not add a single repository-root server: the root is a workspace, not a
|
|
44
|
+
shadcn project, and would make installs target the wrong project.
|
|
45
|
+
|
|
46
|
+
- Use the shadcn MCP to search, inspect, and install registry items when it is
|
|
47
|
+
connected. Prefer the app's configured registries over copied source.
|
|
48
|
+
- Use `pnpm dlx shadcn@latest info --json` for project context; the MCP has no
|
|
49
|
+
equivalent project-info tool.
|
|
50
|
+
- If the MCP is unavailable, use the equivalent `pnpm dlx shadcn@latest`
|
|
51
|
+
command from the same app root and continue following this skill.
|
|
52
|
+
- Keep registry URLs and aliases in that app's `components.json`. Never put
|
|
53
|
+
registry credentials in MCP config or checked-in source.
|
|
54
|
+
|
|
55
|
+
## Design-System Lint Feedback
|
|
56
|
+
|
|
57
|
+
In the framework repository, `pnpm lint` runs Oxlint with `@shadcn/lint` for the
|
|
58
|
+
scoped template UI paths. Run it after UI changes and fix every `shadcn/*`
|
|
59
|
+
finding before handing off. Template packages use the workspace lint
|
|
60
|
+
configuration; do not create a second local lint configuration just for one
|
|
61
|
+
app.
|
|
62
|
+
|
|
63
|
+
Before claiming that shadcn lint ran in another workspace, inspect its root
|
|
64
|
+
`package.json` and `.oxlintrc.json`. The generic generated workspace currently
|
|
65
|
+
exposes `pnpm lint` as formatting-only and does not ship `@shadcn/lint` or the
|
|
66
|
+
framework repository's Oxlint config. In that scaffold, use this skill plus the
|
|
67
|
+
connected shadcn MCP (or the CLI fallback) for design-system guidance, and do
|
|
68
|
+
not report `shadcn/*` checks as having run. If the workspace owner adopts the
|
|
69
|
+
linter, add the dependency, scoped config, and lint script together.
|
|
70
|
+
|
|
71
|
+
The Plan template is intentionally excluded from this rollout. Its files remain
|
|
72
|
+
under `templates/plan/**`, but the root Oxlint ignore list keeps them out of the
|
|
73
|
+
shadcn checks until that template has a separate lint baseline.
|
|
74
|
+
|
|
75
|
+
- `no-restyle`: use the component's `variant`, `size`, and semantic props; put
|
|
76
|
+
layout on a parent and add a component variant only when the design system
|
|
77
|
+
genuinely needs a new treatment.
|
|
78
|
+
- `no-raw-colors`: use the theme tokens in the app's `global.css`, not raw
|
|
79
|
+
Tailwind palette classes or hard-coded color attributes.
|
|
80
|
+
- `no-arbitrary-values`: use the theme scale or a declared token instead of
|
|
81
|
+
one-off bracket values.
|
|
82
|
+
- `no-inline-styles`: use classes or CSS custom properties for dynamic layout;
|
|
83
|
+
keep intentional editor/export exceptions scoped to their owning subsystem.
|
|
84
|
+
- `no-unknown-classes` and `require-static-classes`: use classes Tailwind can
|
|
85
|
+
generate and keep class strings statically discoverable.
|
|
86
|
+
|
|
87
|
+
The local primitive source is the contract boundary. Product code should consume
|
|
88
|
+
its props and variants; update `packages/toolkit/src/ui/` or an app's
|
|
89
|
+
`components/ui/` only when the design-system contract itself changes.
|
|
90
|
+
|
|
32
91
|
## Agent-Native Adapter Rule
|
|
33
92
|
|
|
34
93
|
Pages, routes, and domain components import controls through the app's local UI
|