@sanity/agent-directives 0.0.9 → 0.0.11
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/_chunks-dts/index.d.ts.map +1 -1
- package/dist/_chunks-es/formatters.js +11 -2
- package/dist/_chunks-es/formatters.js.map +1 -1
- package/dist/lib/react.d.ts +4 -0
- package/dist/lib/react.d.ts.map +1 -1
- package/dist/lib/react.js +22 -5
- package/dist/lib/react.js.map +1 -1
- package/dist/lib/streaming.d.ts.map +1 -1
- package/dist/lib/streaming.js +1 -7
- package/dist/lib/streaming.js.map +1 -1
- package/package.json +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/_internal/index.ts"],"mappings":";cASa,uBAAA,EAAuB,CAAA,CAAA,SAAA;;;;;;;;;;;;;cAMvB,sBAAA,EAAsB,CAAA,CAAA,SAAA;;;;;;;;;;cAKtB,uBAAA,EAAuB,CAAA,CAAA,SAAA;;;;;;;cAIvB,mCAAA,EAAmC,CAAA,CAAA,SAAA;;;;;;;cAInC,kBAAA,EAAkB,CAAA,CAAA,SAAA;;;;;;;cAIlB,sBAAA,EAAsB,CAAA,CAAA,SAAA;;;;;;;;;;cAKtB,gBAAA;EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cASA,eAAA;EAAA;;;;;;;KAiBD,aAAA,WAAwB,eAAA,eAA8B,eAAA;AAAA,KAEtD,sBAAA,GAAyB,CAAA,CAAE,KAAA,QAAa,uBAAA;AAAA,KACxC,qBAAA,GAAwB,CAAA,CAAE,KAAA,QAAa,sBAAA;AAAA,KACvC,sBAAA,GAAyB,CAAA,CAAE,KAAA,QAAa,uBAAA;AAAA,KACxC,iBAAA,GAAoB,CAAA,CAAE,KAAA,QAAa,kBAAA;AAAA,KACnC,qBAAA,GAAwB,CAAA,CAAE,KAAA,QAAa,sBAAA;AAAA,KACvC,kCAAA,GAAqC,CAAA,CAAE,KAAA,QAAa,mCAAA;AAAA,UAE/C,iBAAA;EACf,QAAA,EAAU,sBAAA;EACV,OAAA,EAAS,qBAAA;EACT,QAAA,EAAU,sBAAA;EACV,GAAA,EAAK,iBAAA;EACL,OAAA,EAAS,qBAAA;EACT,oBAAA,EAAsB,kCAAA;AAAA;AAAA,UAGP,sBAAA;;;;;EAKf,eAAA;;EAEA,mBAAA;AAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/_internal/index.ts"],"mappings":";cASa,uBAAA,EAAuB,CAAA,CAAA,SAAA;;;;;;;;;;;;;cAMvB,sBAAA,EAAsB,CAAA,CAAA,SAAA;;;;;;;;;;cAKtB,uBAAA,EAAuB,CAAA,CAAA,SAAA;;;;;;;cAIvB,mCAAA,EAAmC,CAAA,CAAA,SAAA;;;;;;;cAInC,kBAAA,EAAkB,CAAA,CAAA,SAAA;;;;;;;cAIlB,sBAAA,EAAsB,CAAA,CAAA,SAAA;;;;;;;;;;cAKtB,gBAAA;EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cASA,eAAA;EAAA;;;;;;;KAiBD,aAAA,WAAwB,eAAA,eAA8B,eAAA;AAAA,KAEtD,sBAAA,GAAyB,CAAA,CAAE,KAAA,QAAa,uBAAA;AAAA,KACxC,qBAAA,GAAwB,CAAA,CAAE,KAAA,QAAa,sBAAA;AAAA,KACvC,sBAAA,GAAyB,CAAA,CAAE,KAAA,QAAa,uBAAA;AAAA,KACxC,iBAAA,GAAoB,CAAA,CAAE,KAAA,QAAa,kBAAA;AAAA,KACnC,qBAAA,GAAwB,CAAA,CAAE,KAAA,QAAa,sBAAA;AAAA,KACvC,kCAAA,GAAqC,CAAA,CAAE,KAAA,QAAa,mCAAA;AAAA,UAE/C,iBAAA;EACf,QAAA,EAAU,sBAAA;EACV,OAAA,EAAS,qBAAA;EACT,QAAA,EAAU,sBAAA;EACV,GAAA,EAAK,iBAAA;EACL,OAAA,EAAS,qBAAA;EACT,oBAAA,EAAsB,kCAAA;AAAA;AAAA,UAGP,sBAAA;;;;;EAKf,eAAA;;EAEA,mBAAA;AAAA;AAAA,iBAkCc,eAAA,CACd,IAAA,EAAM,aAAA,EACN,KAAA,EAAO,MAAA,mBACP,KAAA;AAAA,iBASc,uBAAA,CAAwB,KAAA,EAAO,sBAAA,EAAwB,KAAA;AAAA,iBAIvD,sBAAA,CAAuB,KAAA,EAAO,qBAAA,EAAuB,KAAA;AAAA,iBAIrD,uBAAA,CAAwB,KAAA,EAAO,sBAAA;AAAA,iBAI/B,kBAAA,CAAmB,KAAA,EAAO,iBAAA;AAAA,iBAI1B,sBAAA,CAAuB,KAAA,EAAO,qBAAA;AAAA,iBAI9B,mCAAA,CACd,KAAA,EAAO,kCAAA"}
|
|
@@ -29,7 +29,13 @@ const DocumentDirectiveSchema = z.object({
|
|
|
29
29
|
set: "set",
|
|
30
30
|
changes: "changes",
|
|
31
31
|
requestProjectAccess: "requestProjectAccess"
|
|
32
|
-
}, DIRECTIVE_TYPES = ["textDirective", "leafDirective", "containerDirective"]
|
|
32
|
+
}, DIRECTIVE_TYPES = ["textDirective", "leafDirective", "containerDirective"], BLOCK_DIRECTIVE = /::([a-zA-Z]\w*)(?:\[([^\]]*)\])?\{([^}]*)\}/, INLINE_DIRECTIVE = /:([a-zA-Z]\w*)\[([^\]]*)\]\{([^}]*)\}/;
|
|
33
|
+
function parseDirectiveAttributes(attrString) {
|
|
34
|
+
const attrs = {};
|
|
35
|
+
for (const match of attrString.matchAll(/(\w+)="([^"]*)"/g))
|
|
36
|
+
attrs[match[1]] = match[2];
|
|
37
|
+
return attrs;
|
|
38
|
+
}
|
|
33
39
|
function buildAttributes(params) {
|
|
34
40
|
return Object.entries(params).filter(([, value]) => value != null).map(([key, value]) => `${key}="${value}"`).join(" ");
|
|
35
41
|
}
|
|
@@ -56,11 +62,13 @@ function createRequestProjectAccessDirective(props) {
|
|
|
56
62
|
return formatDirective(DIRECTIVE_NAMES.requestProjectAccess, props);
|
|
57
63
|
}
|
|
58
64
|
export {
|
|
65
|
+
BLOCK_DIRECTIVE,
|
|
59
66
|
ChangesDirectiveSchema,
|
|
60
67
|
DIRECTIVE_NAMES,
|
|
61
68
|
DIRECTIVE_TYPES,
|
|
62
69
|
DirectiveSchemas,
|
|
63
70
|
DocumentDirectiveSchema,
|
|
71
|
+
INLINE_DIRECTIVE,
|
|
64
72
|
ReleaseDirectiveSchema,
|
|
65
73
|
ResourceDirectiveSchema,
|
|
66
74
|
SetDirectiveSchema,
|
|
@@ -70,6 +78,7 @@ export {
|
|
|
70
78
|
createRequestProjectAccessDirective,
|
|
71
79
|
createResourceDirective,
|
|
72
80
|
createSetDirective,
|
|
73
|
-
formatDirective
|
|
81
|
+
formatDirective,
|
|
82
|
+
parseDirectiveAttributes
|
|
74
83
|
};
|
|
75
84
|
//# sourceMappingURL=formatters.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"formatters.js","sources":["../../src/_internal/index.ts"],"sourcesContent":["// This internal module contains all shared code that both the main index\n// and sub-exports can import without causing self-referencing issues with pkg-utils\n\nimport { z } from 'zod'\n\n// ============================================================================\n// Schemas\n// ============================================================================\n\nexport const DocumentDirectiveSchema = z.object({\n id: z.string(),\n type: z.string().optional(),\n source: z.string().optional(),\n})\n\nexport const ReleaseDirectiveSchema = z.object({\n id: z.string(),\n source: z.string().optional(),\n})\n\nexport const ResourceDirectiveSchema = z.object({\n source: z.string(),\n})\n\nexport const RequestProjectAccessDirectiveSchema = z.object({\n source: z.string(),\n})\n\nexport const SetDirectiveSchema = z.object({\n id: z.string(),\n})\n\nexport const ChangesDirectiveSchema = z.object({\n createdCount: z.coerce.number().min(0),\n updatedCount: z.coerce.number().min(0),\n})\n\nexport const DirectiveSchemas = {\n document: DocumentDirectiveSchema,\n release: ReleaseDirectiveSchema,\n resource: ResourceDirectiveSchema,\n set: SetDirectiveSchema,\n changes: ChangesDirectiveSchema,\n requestProjectAccess: RequestProjectAccessDirectiveSchema,\n} as const\n\nexport const DIRECTIVE_NAMES = {\n document: 'document',\n release: 'release',\n resource: 'resource',\n set: 'set',\n changes: 'changes',\n requestProjectAccess: 'requestProjectAccess',\n} as const\n\nexport const DIRECTIVE_TYPES = ['textDirective', 'leafDirective', 'containerDirective'] as const\n\nexport type DirectiveType = (typeof DIRECTIVE_TYPES)[number]\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type DirectiveName = (typeof DIRECTIVE_NAMES)[keyof typeof DIRECTIVE_NAMES]\n\nexport type DocumentDirectiveProps = z.infer<typeof DocumentDirectiveSchema>\nexport type ReleaseDirectiveProps = z.infer<typeof ReleaseDirectiveSchema>\nexport type ResourceDirectiveProps = z.infer<typeof ResourceDirectiveSchema>\nexport type SetDirectiveProps = z.infer<typeof SetDirectiveSchema>\nexport type ChangesDirectiveProps = z.infer<typeof ChangesDirectiveSchema>\nexport type RequestProjectAccessDirectiveProps = z.infer<typeof RequestProjectAccessDirectiveSchema>\n\nexport interface DirectivePropsMap {\n document: DocumentDirectiveProps\n release: ReleaseDirectiveProps\n resource: ResourceDirectiveProps\n set: SetDirectiveProps\n changes: ChangesDirectiveProps\n requestProjectAccess: RequestProjectAccessDirectiveProps\n}\n\nexport interface DefineDirectiveOptions {\n /**\n * Whether this directive requires context/application to render.\n * If true (default), the directive won't render without context.\n */\n requiresContext?: boolean\n /** @deprecated Use `requiresContext` instead */\n requiresApplication?: boolean\n}\n\n// ============================================================================\n// Formatter Functions\n// ============================================================================\n\nfunction buildAttributes(params: Record<string, unknown>): string {\n return Object.entries(params)\n .filter(([, value]) => value !== undefined && value !== null)\n .map(([key, value]) => `${key}=\"${value}\"`)\n .join(' ')\n}\n\nexport function formatDirective(\n name: DirectiveName,\n props: Record<string, unknown>,\n label?: string,\n): string {\n const attrs = buildAttributes(props)\n if (label) {\n return attrs ? `:${name}[${label}]{${attrs}}` : `:${name}[${label}]`\n }\n return attrs ? `::${name}{${attrs}}` : `::${name}`\n}\n\nexport function createDocumentDirective(props: DocumentDirectiveProps, label?: string): string {\n return formatDirective(DIRECTIVE_NAMES.document, props, label)\n}\n\nexport function createReleaseDirective(props: ReleaseDirectiveProps, label?: string): string {\n return formatDirective(DIRECTIVE_NAMES.release, props, label)\n}\n\nexport function createResourceDirective(props: ResourceDirectiveProps): string {\n return formatDirective(DIRECTIVE_NAMES.resource, props)\n}\n\nexport function createSetDirective(props: SetDirectiveProps): string {\n return formatDirective(DIRECTIVE_NAMES.set, props)\n}\n\nexport function createChangesDirective(props: ChangesDirectiveProps): string {\n return formatDirective(DIRECTIVE_NAMES.changes, props)\n}\n\nexport function createRequestProjectAccessDirective(\n props: RequestProjectAccessDirectiveProps,\n): string {\n return formatDirective(DIRECTIVE_NAMES.requestProjectAccess, props)\n}\n"],"names":[],"mappings":";AASO,MAAM,0BAA0B,EAAE,OAAO;AAAA,EAC9C,IAAI,EAAE,OAAA;AAAA,EACN,MAAM,EAAE,OAAA,EAAS,SAAA;AAAA,EACjB,QAAQ,EAAE,OAAA,EAAS,SAAA;AACrB,CAAC,GAEY,yBAAyB,EAAE,OAAO;AAAA,EAC7C,IAAI,EAAE,OAAA;AAAA,EACN,QAAQ,EAAE,OAAA,EAAS,SAAA;AACrB,CAAC,GAEY,0BAA0B,EAAE,OAAO;AAAA,EAC9C,QAAQ,EAAE,OAAA;AACZ,CAAC,GAEY,sCAAsC,EAAE,OAAO;AAAA,EAC1D,QAAQ,EAAE,OAAA;AACZ,CAAC,GAEY,qBAAqB,EAAE,OAAO;AAAA,EACzC,IAAI,EAAE,OAAA;AACR,CAAC,GAEY,yBAAyB,EAAE,OAAO;AAAA,EAC7C,cAAc,EAAE,OAAO,OAAA,EAAS,IAAI,CAAC;AAAA,EACrC,cAAc,EAAE,OAAO,OAAA,EAAS,IAAI,CAAC;AACvC,CAAC,GAEY,mBAAmB;AAAA,EAC9B,UAAU;AAAA,EACV,SAAS;AAAA,EACT,UAAU;AAAA,EACV,KAAK;AAAA,EACL,SAAS;AAAA,EACT,sBAAsB;AACxB,GAEa,kBAAkB;AAAA,EAC7B,UAAU;AAAA,EACV,SAAS;AAAA,EACT,UAAU;AAAA,EACV,KAAK;AAAA,EACL,SAAS;AAAA,EACT,sBAAsB;AACxB,GAEa,kBAAkB,CAAC,iBAAiB,iBAAiB,oBAAoB;
|
|
1
|
+
{"version":3,"file":"formatters.js","sources":["../../src/_internal/index.ts"],"sourcesContent":["// This internal module contains all shared code that both the main index\n// and sub-exports can import without causing self-referencing issues with pkg-utils\n\nimport { z } from 'zod'\n\n// ============================================================================\n// Schemas\n// ============================================================================\n\nexport const DocumentDirectiveSchema = z.object({\n id: z.string(),\n type: z.string().optional(),\n source: z.string().optional(),\n})\n\nexport const ReleaseDirectiveSchema = z.object({\n id: z.string(),\n source: z.string().optional(),\n})\n\nexport const ResourceDirectiveSchema = z.object({\n source: z.string(),\n})\n\nexport const RequestProjectAccessDirectiveSchema = z.object({\n source: z.string(),\n})\n\nexport const SetDirectiveSchema = z.object({\n id: z.string(),\n})\n\nexport const ChangesDirectiveSchema = z.object({\n createdCount: z.coerce.number().min(0),\n updatedCount: z.coerce.number().min(0),\n})\n\nexport const DirectiveSchemas = {\n document: DocumentDirectiveSchema,\n release: ReleaseDirectiveSchema,\n resource: ResourceDirectiveSchema,\n set: SetDirectiveSchema,\n changes: ChangesDirectiveSchema,\n requestProjectAccess: RequestProjectAccessDirectiveSchema,\n} as const\n\nexport const DIRECTIVE_NAMES = {\n document: 'document',\n release: 'release',\n resource: 'resource',\n set: 'set',\n changes: 'changes',\n requestProjectAccess: 'requestProjectAccess',\n} as const\n\nexport const DIRECTIVE_TYPES = ['textDirective', 'leafDirective', 'containerDirective'] as const\n\nexport type DirectiveType = (typeof DIRECTIVE_TYPES)[number]\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type DirectiveName = (typeof DIRECTIVE_NAMES)[keyof typeof DIRECTIVE_NAMES]\n\nexport type DocumentDirectiveProps = z.infer<typeof DocumentDirectiveSchema>\nexport type ReleaseDirectiveProps = z.infer<typeof ReleaseDirectiveSchema>\nexport type ResourceDirectiveProps = z.infer<typeof ResourceDirectiveSchema>\nexport type SetDirectiveProps = z.infer<typeof SetDirectiveSchema>\nexport type ChangesDirectiveProps = z.infer<typeof ChangesDirectiveSchema>\nexport type RequestProjectAccessDirectiveProps = z.infer<typeof RequestProjectAccessDirectiveSchema>\n\nexport interface DirectivePropsMap {\n document: DocumentDirectiveProps\n release: ReleaseDirectiveProps\n resource: ResourceDirectiveProps\n set: SetDirectiveProps\n changes: ChangesDirectiveProps\n requestProjectAccess: RequestProjectAccessDirectiveProps\n}\n\nexport interface DefineDirectiveOptions {\n /**\n * Whether this directive requires context/application to render.\n * If true (default), the directive won't render without context.\n */\n requiresContext?: boolean\n /** @deprecated Use `requiresContext` instead */\n requiresApplication?: boolean\n}\n\n// ============================================================================\n// Directive Regex Patterns\n// ============================================================================\n\n// Matches block directives: ::name{attrs} or ::name[label]{attrs}\n// Captures: (1) name, (2) label or undefined, (3) attribute string\nexport const BLOCK_DIRECTIVE = /::([a-zA-Z]\\w*)(?:\\[([^\\]]*)\\])?\\{([^}]*)\\}/\n\n// Matches inline directives: :name[label]{attrs}\n// Captures: (1) name, (2) label, (3) attribute string\nexport const INLINE_DIRECTIVE = /:([a-zA-Z]\\w*)\\[([^\\]]*)\\]\\{([^}]*)\\}/\n\nexport function parseDirectiveAttributes(attrString: string): Record<string, string> {\n const attrs: Record<string, string> = {}\n for (const match of attrString.matchAll(/(\\w+)=\"([^\"]*)\"/g)) {\n attrs[match[1]] = match[2]\n }\n return attrs\n}\n\n// ============================================================================\n// Formatter Functions\n// ============================================================================\n\nfunction buildAttributes(params: Record<string, unknown>): string {\n return Object.entries(params)\n .filter(([, value]) => value !== undefined && value !== null)\n .map(([key, value]) => `${key}=\"${value}\"`)\n .join(' ')\n}\n\nexport function formatDirective(\n name: DirectiveName,\n props: Record<string, unknown>,\n label?: string,\n): string {\n const attrs = buildAttributes(props)\n if (label) {\n return attrs ? `:${name}[${label}]{${attrs}}` : `:${name}[${label}]`\n }\n return attrs ? `::${name}{${attrs}}` : `::${name}`\n}\n\nexport function createDocumentDirective(props: DocumentDirectiveProps, label?: string): string {\n return formatDirective(DIRECTIVE_NAMES.document, props, label)\n}\n\nexport function createReleaseDirective(props: ReleaseDirectiveProps, label?: string): string {\n return formatDirective(DIRECTIVE_NAMES.release, props, label)\n}\n\nexport function createResourceDirective(props: ResourceDirectiveProps): string {\n return formatDirective(DIRECTIVE_NAMES.resource, props)\n}\n\nexport function createSetDirective(props: SetDirectiveProps): string {\n return formatDirective(DIRECTIVE_NAMES.set, props)\n}\n\nexport function createChangesDirective(props: ChangesDirectiveProps): string {\n return formatDirective(DIRECTIVE_NAMES.changes, props)\n}\n\nexport function createRequestProjectAccessDirective(\n props: RequestProjectAccessDirectiveProps,\n): string {\n return formatDirective(DIRECTIVE_NAMES.requestProjectAccess, props)\n}\n"],"names":[],"mappings":";AASO,MAAM,0BAA0B,EAAE,OAAO;AAAA,EAC9C,IAAI,EAAE,OAAA;AAAA,EACN,MAAM,EAAE,OAAA,EAAS,SAAA;AAAA,EACjB,QAAQ,EAAE,OAAA,EAAS,SAAA;AACrB,CAAC,GAEY,yBAAyB,EAAE,OAAO;AAAA,EAC7C,IAAI,EAAE,OAAA;AAAA,EACN,QAAQ,EAAE,OAAA,EAAS,SAAA;AACrB,CAAC,GAEY,0BAA0B,EAAE,OAAO;AAAA,EAC9C,QAAQ,EAAE,OAAA;AACZ,CAAC,GAEY,sCAAsC,EAAE,OAAO;AAAA,EAC1D,QAAQ,EAAE,OAAA;AACZ,CAAC,GAEY,qBAAqB,EAAE,OAAO;AAAA,EACzC,IAAI,EAAE,OAAA;AACR,CAAC,GAEY,yBAAyB,EAAE,OAAO;AAAA,EAC7C,cAAc,EAAE,OAAO,OAAA,EAAS,IAAI,CAAC;AAAA,EACrC,cAAc,EAAE,OAAO,OAAA,EAAS,IAAI,CAAC;AACvC,CAAC,GAEY,mBAAmB;AAAA,EAC9B,UAAU;AAAA,EACV,SAAS;AAAA,EACT,UAAU;AAAA,EACV,KAAK;AAAA,EACL,SAAS;AAAA,EACT,sBAAsB;AACxB,GAEa,kBAAkB;AAAA,EAC7B,UAAU;AAAA,EACV,SAAS;AAAA,EACT,UAAU;AAAA,EACV,KAAK;AAAA,EACL,SAAS;AAAA,EACT,sBAAsB;AACxB,GAEa,kBAAkB,CAAC,iBAAiB,iBAAiB,oBAAoB,GA0CzE,kBAAkB,+CAIlB,mBAAmB;AAEzB,SAAS,yBAAyB,YAA4C;AACnF,QAAM,QAAgC,CAAA;AACtC,aAAW,SAAS,WAAW,SAAS,kBAAkB;AACxD,UAAM,MAAM,CAAC,CAAC,IAAI,MAAM,CAAC;AAE3B,SAAO;AACT;AAMA,SAAS,gBAAgB,QAAyC;AAChE,SAAO,OAAO,QAAQ,MAAM,EACzB,OAAO,CAAC,CAAA,EAAG,KAAK,MAA6B,SAAU,IAAI,EAC3D,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM,GAAG,GAAG,KAAK,KAAK,GAAG,EACzC,KAAK,GAAG;AACb;AAEO,SAAS,gBACd,MACA,OACA,OACQ;AACR,QAAM,QAAQ,gBAAgB,KAAK;AACnC,SAAI,QACK,QAAQ,IAAI,IAAI,IAAI,KAAK,KAAK,KAAK,MAAM,IAAI,IAAI,IAAI,KAAK,MAE5D,QAAQ,KAAK,IAAI,IAAI,KAAK,MAAM,KAAK,IAAI;AAClD;AAEO,SAAS,wBAAwB,OAA+B,OAAwB;AAC7F,SAAO,gBAAgB,gBAAgB,UAAU,OAAO,KAAK;AAC/D;AAEO,SAAS,uBAAuB,OAA8B,OAAwB;AAC3F,SAAO,gBAAgB,gBAAgB,SAAS,OAAO,KAAK;AAC9D;AAEO,SAAS,wBAAwB,OAAuC;AAC7E,SAAO,gBAAgB,gBAAgB,UAAU,KAAK;AACxD;AAEO,SAAS,mBAAmB,OAAkC;AACnE,SAAO,gBAAgB,gBAAgB,KAAK,KAAK;AACnD;AAEO,SAAS,uBAAuB,OAAsC;AAC3E,SAAO,gBAAgB,gBAAgB,SAAS,KAAK;AACvD;AAEO,SAAS,oCACd,OACQ;AACR,SAAO,gBAAgB,gBAAgB,sBAAsB,KAAK;AACpE;"}
|
package/dist/lib/react.d.ts
CHANGED
|
@@ -32,6 +32,10 @@ type DirectiveComponentProps<TName extends DirectiveName> = DirectiveProps<TName
|
|
|
32
32
|
declare function remarkAgentDirectives(this: Processor): (tree: Node) => void;
|
|
33
33
|
interface DirectiveKitOptions<TApplication> {
|
|
34
34
|
useApplication?: (source: string | undefined) => TApplication | undefined;
|
|
35
|
+
renderApplicationUnavailable?: ComponentType<{
|
|
36
|
+
name: DirectiveName;
|
|
37
|
+
source?: string;
|
|
38
|
+
}>;
|
|
35
39
|
}
|
|
36
40
|
/**
|
|
37
41
|
* Creates a directive kit with application injection.
|
package/dist/lib/react.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react.d.ts","names":[],"sources":["../../src/lib/react.tsx"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"react.d.ts","names":[],"sources":["../../src/lib/react.tsx"],"mappings":";;;;KAuBK,cAAA,WAAyB,aAAA,IAAiB,iBAAA,CAAkB,CAAA;AAAA,UAEhD,SAAA;EACf,IAAA;IAAS,UAAA,GAAa,MAAA;EAAA;AAAA;AAAA,KAGZ,oBAAA,eACI,aAAA,2EAGZ,cAAA,CAAe,KAAA;EACjB,QAAA;AAAA,KACG,oBAAA,gBACC,YAAA;EAEI,WAAA,EAAa,YAAA;AAAA;AAAA,KAKX,uBAAA,eAAsC,aAAA,IAAiB,cAAA,CAAe,KAAA;EAChF,MAAA;AAAA,IACE,SAAA;AApBJ;;;;;;;;;AAIA;;;;AAJA,iBA2OgB,qBAAA,CAAsB,IAAA,EAAM,SAAA,IAAS,IAAA,EAlKrC,IAAA;AAAA,UAoLC,mBAAA;EACf,cAAA,IAAkB,MAAA,yBAA+B,YAAA;EACjD,4BAAA,GAA+B,aAAA;IAAgB,IAAA,EAAM,aAAA;IAAe,MAAA;EAAA;AAAA;;;;;;;;;;;;;AA7OtE;;;;iBAgQgB,kBAAA,0BAAA,CACd,OAAA,GAAS,mBAAA,CAAoB,YAAA;kCAKb,aAAA,+CACsB,IAAA,EAE9B,KAAA,EAAK,MAAA,GACF,KAAA,EAAO,oBAAA,CAAqB,KAAA,EAAO,YAAA,EAAc,oBAAA,MAA0B,SAAA,EAAS,gBAAA,GAC3E,sBAAA,KACjB,aAAA,CAAc,uBAAA,CAAwB,KAAA;AAAA"}
|
package/dist/lib/react.js
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
1
2
|
import { pascalCase } from "es-toolkit";
|
|
2
3
|
import { directiveFromMarkdown, directiveToMarkdown } from "mdast-util-directive";
|
|
3
4
|
import { directive } from "micromark-extension-directive";
|
|
4
5
|
import { visit } from "unist-util-visit";
|
|
5
|
-
import { DIRECTIVE_TYPES, DirectiveSchemas } from "../_chunks-es/formatters.js";
|
|
6
|
+
import { DIRECTIVE_NAMES, DIRECTIVE_TYPES, BLOCK_DIRECTIVE, parseDirectiveAttributes, DirectiveSchemas } from "../_chunks-es/formatters.js";
|
|
6
7
|
const isDirectiveNode = (node) => "name" in node && typeof node.name == "string" && /^[a-zA-Z-]+$/.test(node.name) && DIRECTIVE_TYPES.includes(node.type), isListItemWithOnlyDirective = (node) => {
|
|
7
8
|
if (node.type !== "listItem") return !1;
|
|
8
9
|
const children = node.children || [];
|
|
9
10
|
return children.length === 1 && children[0].type === "leafDirective";
|
|
10
|
-
};
|
|
11
|
+
}, KNOWN_DIRECTIVE_NAMES = new Set(Object.values(DIRECTIVE_NAMES));
|
|
12
|
+
function getTextContent(node) {
|
|
13
|
+
return typeof node.value == "string" ? node.value : node.children ? node.children.map((child) => getTextContent(child)).join("") : "";
|
|
14
|
+
}
|
|
11
15
|
function remarkDirectivesTransform() {
|
|
12
16
|
return (tree) => {
|
|
13
17
|
visit(
|
|
@@ -26,7 +30,19 @@ function remarkDirectivesTransform() {
|
|
|
26
30
|
}
|
|
27
31
|
}
|
|
28
32
|
}
|
|
29
|
-
), visit(
|
|
33
|
+
), visit(tree, "paragraph", (node, index, parent) => {
|
|
34
|
+
const match = getTextContent(node).match(BLOCK_DIRECTIVE);
|
|
35
|
+
if (!match) return;
|
|
36
|
+
const [, name, label, attrsString] = match;
|
|
37
|
+
if (!KNOWN_DIRECTIVE_NAMES.has(name)) return;
|
|
38
|
+
const directiveNode = {
|
|
39
|
+
type: "leafDirective",
|
|
40
|
+
name,
|
|
41
|
+
attributes: parseDirectiveAttributes(attrsString),
|
|
42
|
+
children: label ? [{ type: "text", name: "", value: label }] : []
|
|
43
|
+
}, parentNode = parent;
|
|
44
|
+
parentNode?.children && typeof index == "number" && (parentNode.children[index] = directiveNode);
|
|
45
|
+
}), visit(
|
|
30
46
|
tree,
|
|
31
47
|
"list",
|
|
32
48
|
(node, index, parent) => {
|
|
@@ -74,12 +90,13 @@ function remarkAgentDirectives() {
|
|
|
74
90
|
return data.micromarkExtensions || (data.micromarkExtensions = []), data.fromMarkdownExtensions || (data.fromMarkdownExtensions = []), data.toMarkdownExtensions || (data.toMarkdownExtensions = []), data.micromarkExtensions.push(directive()), data.fromMarkdownExtensions.push(directiveFromMarkdown()), data.toMarkdownExtensions.push(directiveToMarkdown()), remarkDirectivesTransform();
|
|
75
91
|
}
|
|
76
92
|
function createDirectiveKit(options = {}) {
|
|
77
|
-
const { useApplication } = options;
|
|
93
|
+
const { useApplication, renderApplicationUnavailable: Unavailable } = options;
|
|
78
94
|
function defineDirective(name, render, directiveOptions = {}) {
|
|
79
95
|
const requiresContext = directiveOptions.requiresContext ?? directiveOptions.requiresApplication ?? !0;
|
|
80
96
|
function Directive(props) {
|
|
81
97
|
const { node, source, ...rest } = props, application = useApplication?.(source);
|
|
82
|
-
if (requiresContext && useApplication && !application)
|
|
98
|
+
if (requiresContext && useApplication && !application)
|
|
99
|
+
return Unavailable ? /* @__PURE__ */ jsx(Unavailable, { name, source }) : null;
|
|
83
100
|
const merged = { ...node?.properties ?? {}, ...rest }, result = DirectiveSchemas[name].safeParse(merged);
|
|
84
101
|
if (!result.success)
|
|
85
102
|
return console.error(`Directive "${name}" validation error:`, result.error.format()), null;
|
package/dist/lib/react.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react.js","sources":["../../src/lib/react.tsx"],"sourcesContent":["import { pascalCase } from 'es-toolkit'\nimport { directiveFromMarkdown, directiveToMarkdown } from 'mdast-util-directive'\nimport { directive } from 'micromark-extension-directive'\nimport type { ComponentType, ReactNode } from 'react'\nimport type { Processor } from 'unified'\nimport type { Node } from 'unist'\nimport { visit } from 'unist-util-visit'\nimport {\n type DefineDirectiveOptions,\n DIRECTIVE_TYPES,\n type DirectiveName,\n type DirectivePropsMap,\n DirectiveSchemas,\n type DirectiveType,\n} from '../_internal'\n\n// ============================================================================\n// Types\n// ============================================================================\n\ntype DirectiveProps<T extends DirectiveName> = DirectivePropsMap[T]\n\nexport interface NodeProps {\n node?: { properties?: Record<string, unknown> }\n}\n\nexport type DirectiveRenderProps<\n TName extends DirectiveName,\n TApplication = undefined,\n TRequiresApplication extends boolean = true,\n> = DirectiveProps<TName> & {\n isInline: boolean\n} & (TRequiresApplication extends true\n ? TApplication extends undefined\n ? {}\n : { application: TApplication }\n : {})\n\nexport type { DefineDirectiveOptions }\n\nexport type DirectiveComponentProps<TName extends DirectiveName> = DirectiveProps<TName> & {\n source?: string\n} & NodeProps\n\n// ============================================================================\n// Remark Plugin Types\n// ============================================================================\n\ninterface DirectiveNode extends Node {\n type: 'textDirective' | 'leafDirective' | 'containerDirective'\n name: string\n attributes?: Record<string, string>\n children?: DirectiveNode[]\n value?: string\n data?: {\n hName?: string\n hProperties?: Record<string, unknown>\n }\n}\n\nconst isDirectiveNode = (node: Node): node is DirectiveNode =>\n 'name' in node &&\n typeof (node as DirectiveNode).name === 'string' &&\n /^[a-zA-Z-]+$/.test((node as DirectiveNode).name) &&\n DIRECTIVE_TYPES.includes(node.type as DirectiveType)\n\nconst isListItemWithOnlyDirective = (node: Node): boolean => {\n if (node.type !== 'listItem') return false\n const children = (node as DirectiveNode).children || []\n return children.length === 1 && children[0].type === 'leafDirective'\n}\n\n// ============================================================================\n// Remark Plugin\n// ============================================================================\n\n/**\n * Remark plugin that transforms directive syntax into React components.\n *\n * - Validates directive names (letters and hyphens only)\n * - Extracts directives from list items\n * - Groups consecutive leaf directives into DirectivesStack wrappers\n * - Filters incomplete streaming directives\n * - Converts directive names to PascalCase component names\n */\nfunction remarkDirectivesTransform() {\n return (tree: Node) => {\n // Remove invalid directive nodes (e.g., \":02\" in \"14:02\")\n visit(\n tree,\n DIRECTIVE_TYPES,\n (node: Node, index: number | undefined, parent: Node | undefined) => {\n if (!isDirectiveNode(node)) {\n const parentNode = parent as DirectiveNode | undefined\n if (parentNode?.children && typeof index === 'number') {\n const nodeName = 'name' in node ? String((node as DirectiveNode).name) : ''\n parentNode.children[index] = {\n type: 'text' as DirectiveType,\n name: '',\n value: `:${nodeName}`,\n } as DirectiveNode\n }\n }\n },\n )\n\n // Extract directives from list items\n visit(\n tree,\n 'list',\n (node: Node, index: number | undefined, parent: Node | undefined): number | undefined => {\n const listNode = node as DirectiveNode\n if (!listNode.children) return undefined\n\n const transformed: DirectiveNode[] = []\n let hasTransformed = false\n\n for (const child of listNode.children) {\n if (isListItemWithOnlyDirective(child) && child.children?.[0]) {\n transformed.push(child.children[0])\n hasTransformed = true\n } else {\n transformed.push(child)\n }\n }\n\n const parentNode = parent as DirectiveNode | undefined\n if (hasTransformed && transformed.every((n) => isDirectiveNode(n))) {\n if (parentNode?.children && typeof index === 'number') {\n parentNode.children.splice(index, 1, ...transformed)\n return index + transformed.length\n }\n }\n return undefined\n },\n )\n\n // Group consecutive leaf directives into DirectivesStack\n visit(tree, (node: Node) => {\n const dirNode = node as DirectiveNode\n if (!dirNode.children?.length || dirNode.data?.hName === 'DirectivesStack') return\n\n const newChildren: DirectiveNode[] = []\n let group: DirectiveNode[] = []\n\n for (const child of dirNode.children) {\n if (isDirectiveNode(child) && child.type === 'leafDirective') {\n group.push(child)\n } else {\n if (group.length > 0) {\n newChildren.push({\n type: 'leafDirective',\n name: 'DirectivesStack',\n data: { hName: 'DirectivesStack' },\n children: group,\n })\n group = []\n }\n newChildren.push(child)\n }\n }\n\n if (group.length > 0) {\n newChildren.push({\n type: 'leafDirective',\n name: 'DirectivesStack',\n data: { hName: 'DirectivesStack' },\n children: group,\n })\n }\n\n dirNode.children = newChildren\n })\n\n // Filter incomplete streaming directives\n visit(tree, 'text', (node: Node) => {\n const textNode = node as DirectiveNode\n if (typeof textNode.value === 'string') {\n if (textNode.value.match(/::?\\w+[{[]/) && !textNode.value.match(/::?\\w+[{[].+[}\\]]/)) {\n textNode.value = ''\n }\n }\n })\n\n // Transform directives to React components\n visit(tree, DIRECTIVE_TYPES, (node: Node) => {\n if (!isDirectiveNode(node)) return\n\n node.data = node.data || {}\n node.data.hName = pascalCase(node.name)\n node.data.hProperties = {\n ...node.attributes,\n isInline: node.type === 'textDirective',\n }\n })\n }\n}\n\ninterface RemarkData {\n micromarkExtensions?: unknown[]\n fromMarkdownExtensions?: unknown[]\n toMarkdownExtensions?: unknown[]\n}\n\n/**\n * Remark plugin for agent directive support.\n * Parses and transforms directive syntax into React components.\n *\n * @example\n * ```tsx\n * import { remarkAgentDirectives } from '@sanity/agent-directives/react'\n *\n * <ReactMarkdown remarkPlugins={[remarkAgentDirectives]}>\n * {content}\n * </ReactMarkdown>\n * ```\n */\nexport function remarkAgentDirectives(this: Processor) {\n const data = this.data() as RemarkData\n\n if (!data.micromarkExtensions) data.micromarkExtensions = []\n if (!data.fromMarkdownExtensions) data.fromMarkdownExtensions = []\n if (!data.toMarkdownExtensions) data.toMarkdownExtensions = []\n\n data.micromarkExtensions.push(directive())\n data.fromMarkdownExtensions.push(directiveFromMarkdown())\n data.toMarkdownExtensions.push(directiveToMarkdown())\n\n return remarkDirectivesTransform()\n}\n\n// ============================================================================\n// Directive Kit\n// ============================================================================\n\nexport interface DirectiveKitOptions<TApplication> {\n useApplication?: (source: string | undefined) => TApplication | undefined\n}\n\n/**\n * Creates a directive kit with application injection.\n *\n * @example\n * ```tsx\n * // kit.ts\n * export const { defineDirective } = createDirectiveKit<Application>({\n * useApplication: (source) => useThreadApplication(source),\n * })\n *\n * // Document.tsx\n * export const DocumentDirective = defineDirective('document', ({ id, application }) => {\n * return <DocumentCard id={id} app={application} />\n * })\n * ```\n */\nexport function createDirectiveKit<TApplication = undefined>(\n options: DirectiveKitOptions<TApplication> = {},\n) {\n const { useApplication } = options\n\n function defineDirective<\n TName extends DirectiveName,\n TRequiresApplication extends boolean = true,\n >(\n name: TName,\n render: (props: DirectiveRenderProps<TName, TApplication, TRequiresApplication>) => ReactNode,\n directiveOptions: DefineDirectiveOptions = {},\n ): ComponentType<DirectiveComponentProps<TName>> {\n const requiresContext =\n directiveOptions.requiresContext ?? directiveOptions.requiresApplication ?? true\n\n function Directive(props: DirectiveComponentProps<TName>): ReactNode {\n const { node, source, ...rest } = props\n\n const application = useApplication?.(source)\n if (requiresContext && useApplication && !application) return null\n\n const merged = { ...(node?.properties ?? {}), ...rest }\n\n const schema = DirectiveSchemas[name]\n const result = schema.safeParse(merged)\n if (!result.success) {\n console.error(`Directive \"${name}\" validation error:`, result.error.format())\n return null\n }\n\n const renderProps = {\n ...(result.data as DirectiveProps<TName>),\n ...(requiresContext && useApplication ? { application } : {}),\n isInline: merged.isInline === true,\n } as DirectiveRenderProps<TName, TApplication, TRequiresApplication>\n\n return render(renderProps)\n }\n\n Directive.displayName = `Directive(${name})`\n return Directive\n }\n\n return { defineDirective }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"react.js","sources":["../../src/lib/react.tsx"],"sourcesContent":["import { pascalCase } from 'es-toolkit'\nimport { directiveFromMarkdown, directiveToMarkdown } from 'mdast-util-directive'\nimport { directive } from 'micromark-extension-directive'\nimport type { ComponentType, ReactNode } from 'react'\nimport type { Processor } from 'unified'\nimport type { Node } from 'unist'\nimport { visit } from 'unist-util-visit'\nimport {\n BLOCK_DIRECTIVE,\n type DefineDirectiveOptions,\n DIRECTIVE_NAMES,\n DIRECTIVE_TYPES,\n type DirectiveName,\n type DirectivePropsMap,\n DirectiveSchemas,\n type DirectiveType,\n parseDirectiveAttributes,\n} from '../_internal'\n\n// ============================================================================\n// Types\n// ============================================================================\n\ntype DirectiveProps<T extends DirectiveName> = DirectivePropsMap[T]\n\nexport interface NodeProps {\n node?: { properties?: Record<string, unknown> }\n}\n\nexport type DirectiveRenderProps<\n TName extends DirectiveName,\n TApplication = undefined,\n TRequiresApplication extends boolean = true,\n> = DirectiveProps<TName> & {\n isInline: boolean\n} & (TRequiresApplication extends true\n ? TApplication extends undefined\n ? {}\n : { application: TApplication }\n : {})\n\nexport type { DefineDirectiveOptions }\n\nexport type DirectiveComponentProps<TName extends DirectiveName> = DirectiveProps<TName> & {\n source?: string\n} & NodeProps\n\n// ============================================================================\n// Remark Plugin Types\n// ============================================================================\n\ninterface DirectiveNode extends Node {\n type: 'textDirective' | 'leafDirective' | 'containerDirective'\n name: string\n attributes?: Record<string, string>\n children?: DirectiveNode[]\n value?: string\n data?: {\n hName?: string\n hProperties?: Record<string, unknown>\n }\n}\n\nconst isDirectiveNode = (node: Node): node is DirectiveNode =>\n 'name' in node &&\n typeof (node as DirectiveNode).name === 'string' &&\n /^[a-zA-Z-]+$/.test((node as DirectiveNode).name) &&\n DIRECTIVE_TYPES.includes(node.type as DirectiveType)\n\nconst isListItemWithOnlyDirective = (node: Node): boolean => {\n if (node.type !== 'listItem') return false\n const children = (node as DirectiveNode).children || []\n return children.length === 1 && children[0].type === 'leafDirective'\n}\n\nconst KNOWN_DIRECTIVE_NAMES: Set<string> = new Set(Object.values(DIRECTIVE_NAMES))\n\nfunction getTextContent(node: DirectiveNode): string {\n if (typeof node.value === 'string') return node.value\n if (!node.children) return ''\n return node.children.map((child) => getTextContent(child)).join('')\n}\n\n// ============================================================================\n// Remark Plugin\n// ============================================================================\n\n/**\n * Remark plugin that transforms directive syntax into React components.\n *\n * - Validates directive names (letters and hyphens only)\n * - Rescues unparsed block directives from paragraph text (lists, trailing text, streaming junk)\n * - Extracts directives from list items\n * - Groups consecutive leaf directives into DirectivesStack wrappers\n * - Filters incomplete streaming directives\n * - Converts directive names to PascalCase component names\n */\nfunction remarkDirectivesTransform() {\n return (tree: Node) => {\n // Remove invalid directive nodes (e.g., \":02\" in \"14:02\")\n visit(\n tree,\n DIRECTIVE_TYPES,\n (node: Node, index: number | undefined, parent: Node | undefined) => {\n if (!isDirectiveNode(node)) {\n const parentNode = parent as DirectiveNode | undefined\n if (parentNode?.children && typeof index === 'number') {\n const nodeName = 'name' in node ? String((node as DirectiveNode).name) : ''\n parentNode.children[index] = {\n type: 'text' as DirectiveType,\n name: '',\n value: `:${nodeName}`,\n } as DirectiveNode\n }\n }\n },\n )\n\n // Rescue unparsed block directives from paragraph text.\n // When the LLM outputs directives inside lists, with trailing text, or when\n // the streaming preprocessor appends junk characters (e.g. closing a `_`),\n // micromark cannot parse them as leaf directives because they require their\n // own line with no surrounding content. This step finds block directive\n // patterns in paragraph text and promotes them to proper directive nodes,\n // discarding any surrounding text on the same line.\n visit(tree, 'paragraph', (node: Node, index: number | undefined, parent: Node | undefined) => {\n const paragraphNode = node as DirectiveNode\n const text = getTextContent(paragraphNode)\n const match = text.match(BLOCK_DIRECTIVE)\n if (!match) return\n\n const [, name, label, attrsString] = match\n if (!KNOWN_DIRECTIVE_NAMES.has(name)) return\n\n const directiveNode: DirectiveNode = {\n type: 'leafDirective',\n name,\n attributes: parseDirectiveAttributes(attrsString),\n children: label\n ? [{ type: 'text' as DirectiveType, name: '', value: label } as DirectiveNode]\n : [],\n }\n\n const parentNode = parent as DirectiveNode | undefined\n if (parentNode?.children && typeof index === 'number') {\n parentNode.children[index] = directiveNode\n }\n })\n\n // Extract directives from list items\n visit(\n tree,\n 'list',\n (node: Node, index: number | undefined, parent: Node | undefined): number | undefined => {\n const listNode = node as DirectiveNode\n if (!listNode.children) return undefined\n\n const transformed: DirectiveNode[] = []\n let hasTransformed = false\n\n for (const child of listNode.children) {\n if (isListItemWithOnlyDirective(child) && child.children?.[0]) {\n transformed.push(child.children[0])\n hasTransformed = true\n } else {\n transformed.push(child)\n }\n }\n\n const parentNode = parent as DirectiveNode | undefined\n if (hasTransformed && transformed.every((n) => isDirectiveNode(n))) {\n if (parentNode?.children && typeof index === 'number') {\n parentNode.children.splice(index, 1, ...transformed)\n return index + transformed.length\n }\n }\n return undefined\n },\n )\n\n // Group consecutive leaf directives into DirectivesStack\n visit(tree, (node: Node) => {\n const dirNode = node as DirectiveNode\n if (!dirNode.children?.length || dirNode.data?.hName === 'DirectivesStack') return\n\n const newChildren: DirectiveNode[] = []\n let group: DirectiveNode[] = []\n\n for (const child of dirNode.children) {\n if (isDirectiveNode(child) && child.type === 'leafDirective') {\n group.push(child)\n } else {\n if (group.length > 0) {\n newChildren.push({\n type: 'leafDirective',\n name: 'DirectivesStack',\n data: { hName: 'DirectivesStack' },\n children: group,\n })\n group = []\n }\n newChildren.push(child)\n }\n }\n\n if (group.length > 0) {\n newChildren.push({\n type: 'leafDirective',\n name: 'DirectivesStack',\n data: { hName: 'DirectivesStack' },\n children: group,\n })\n }\n\n dirNode.children = newChildren\n })\n\n // Filter incomplete streaming directives\n visit(tree, 'text', (node: Node) => {\n const textNode = node as DirectiveNode\n if (typeof textNode.value === 'string') {\n if (textNode.value.match(/::?\\w+[{[]/) && !textNode.value.match(/::?\\w+[{[].+[}\\]]/)) {\n textNode.value = ''\n }\n }\n })\n\n // Transform directives to React components\n visit(tree, DIRECTIVE_TYPES, (node: Node) => {\n if (!isDirectiveNode(node)) return\n\n node.data = node.data || {}\n node.data.hName = pascalCase(node.name)\n node.data.hProperties = {\n ...node.attributes,\n isInline: node.type === 'textDirective',\n }\n })\n }\n}\n\ninterface RemarkData {\n micromarkExtensions?: unknown[]\n fromMarkdownExtensions?: unknown[]\n toMarkdownExtensions?: unknown[]\n}\n\n/**\n * Remark plugin for agent directive support.\n * Parses and transforms directive syntax into React components.\n *\n * @example\n * ```tsx\n * import { remarkAgentDirectives } from '@sanity/agent-directives/react'\n *\n * <ReactMarkdown remarkPlugins={[remarkAgentDirectives]}>\n * {content}\n * </ReactMarkdown>\n * ```\n */\nexport function remarkAgentDirectives(this: Processor) {\n const data = this.data() as RemarkData\n\n if (!data.micromarkExtensions) data.micromarkExtensions = []\n if (!data.fromMarkdownExtensions) data.fromMarkdownExtensions = []\n if (!data.toMarkdownExtensions) data.toMarkdownExtensions = []\n\n data.micromarkExtensions.push(directive())\n data.fromMarkdownExtensions.push(directiveFromMarkdown())\n data.toMarkdownExtensions.push(directiveToMarkdown())\n\n return remarkDirectivesTransform()\n}\n\n// ============================================================================\n// Directive Kit\n// ============================================================================\n\nexport interface DirectiveKitOptions<TApplication> {\n useApplication?: (source: string | undefined) => TApplication | undefined\n renderApplicationUnavailable?: ComponentType<{ name: DirectiveName; source?: string }>\n}\n\n/**\n * Creates a directive kit with application injection.\n *\n * @example\n * ```tsx\n * // kit.ts\n * export const { defineDirective } = createDirectiveKit<Application>({\n * useApplication: (source) => useThreadApplication(source),\n * })\n *\n * // Document.tsx\n * export const DocumentDirective = defineDirective('document', ({ id, application }) => {\n * return <DocumentCard id={id} app={application} />\n * })\n * ```\n */\nexport function createDirectiveKit<TApplication = undefined>(\n options: DirectiveKitOptions<TApplication> = {},\n) {\n const { useApplication, renderApplicationUnavailable: Unavailable } = options\n\n function defineDirective<\n TName extends DirectiveName,\n TRequiresApplication extends boolean = true,\n >(\n name: TName,\n render: (props: DirectiveRenderProps<TName, TApplication, TRequiresApplication>) => ReactNode,\n directiveOptions: DefineDirectiveOptions = {},\n ): ComponentType<DirectiveComponentProps<TName>> {\n const requiresContext =\n directiveOptions.requiresContext ?? directiveOptions.requiresApplication ?? true\n\n function Directive(props: DirectiveComponentProps<TName>): ReactNode {\n const { node, source, ...rest } = props\n\n const application = useApplication?.(source)\n if (requiresContext && useApplication && !application) {\n return Unavailable ? <Unavailable name={name} source={source} /> : null\n }\n\n const merged = { ...(node?.properties ?? {}), ...rest }\n\n const schema = DirectiveSchemas[name]\n const result = schema.safeParse(merged)\n if (!result.success) {\n console.error(`Directive \"${name}\" validation error:`, result.error.format())\n return null\n }\n\n const renderProps = {\n ...(result.data as DirectiveProps<TName>),\n ...(requiresContext && useApplication ? { application } : {}),\n isInline: merged.isInline === true,\n } as DirectiveRenderProps<TName, TApplication, TRequiresApplication>\n\n return render(renderProps)\n }\n\n Directive.displayName = `Directive(${name})`\n return Directive\n }\n\n return { defineDirective }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"streaming.d.ts","names":[],"sources":["../../src/lib/streaming.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"streaming.d.ts","names":[],"sources":["../../src/lib/streaming.ts"],"mappings":"AA+BA;;;;;;;;;;AA2BA;;;;;;;;;;AAUA;;;;;;;;AArCA,UAAiB,qBAAA;EA2CA;;;;;;;EAnCf,OAAA,CAAQ,KAAA;EAmDC;;;;AAsBX;;EAjEE,KAAA;EAiE6C;;AAmL/C;EA/OE,KAAA;AAAA;;;;UAMe,eAAA;EACf,IAAA;EACA,UAAA,EAAY,MAAA;EACZ,QAAA;AAAA;;;;;KAOU,kBAAA,IAAsB,SAAA,EAAW,eAAA,KAAoB,OAAA;;;;;UAMhD,wBAAA;;;;;;;;EAQf,OAAA,CAAQ,KAAA,WAAgB,OAAA;;;;;;;EAQxB,KAAA,IAAS,OAAA;;;;EAKT,KAAA;AAAA;;;;;;;;;;;;;;;iBAiBc,2BAAA,CAAA,GAA+B,qBAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAmL/B,8BAAA,CACd,gBAAA,EAAkB,kBAAA,GACjB,wBAAA"}
|
package/dist/lib/streaming.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { INLINE_DIRECTIVE, BLOCK_DIRECTIVE, parseDirectiveAttributes } from "../_chunks-es/formatters.js";
|
|
1
2
|
function createDirectiveStreamBuffer() {
|
|
2
3
|
let buffer = "";
|
|
3
4
|
function process(chunk) {
|
|
@@ -61,13 +62,6 @@ function createDirectiveStreamBuffer() {
|
|
|
61
62
|
reset
|
|
62
63
|
};
|
|
63
64
|
}
|
|
64
|
-
function parseDirectiveAttributes(attrString) {
|
|
65
|
-
const attrs = {};
|
|
66
|
-
for (const match of attrString.matchAll(/(\w+)="([^"]*)"/g))
|
|
67
|
-
attrs[match[1]] = match[2];
|
|
68
|
-
return attrs;
|
|
69
|
-
}
|
|
70
|
-
const BLOCK_DIRECTIVE = /::([a-zA-Z]\w*)(?:\[([^\]]*)\])?\{([^}]*)\}/, INLINE_DIRECTIVE = /:([a-zA-Z]\w*)\[([^\]]*)\]\{([^}]*)\}/;
|
|
71
65
|
function looksLikeBlockDirectiveStart(text) {
|
|
72
66
|
return text.startsWith("::") ? text.length === 2 ? !0 : /[a-zA-Z[{]/.test(text[2]) : !1;
|
|
73
67
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"streaming.js","sources":["../../src/lib/streaming.ts"],"sourcesContent":["/**\n * Streaming utilities for handling directive syntax during real-time text streaming.\n *\n * When streaming text that contains directives (e.g., `::document{id=\"123\"}`),\n * we need to buffer potential directive content and strip complete directives\n * from the streamed output, since they'll be rendered as blocks/components\n * at the end of the stream.\n *\n * @example\n * ```ts\n * import { createDirectiveStreamBuffer } from '@sanity/agent-directives/streaming'\n *\n * const buffer = createDirectiveStreamBuffer()\n *\n * for await (const chunk of stream) {\n * const textToStream = buffer.process(chunk)\n * if (textToStream) {\n * await sendToClient(textToStream)\n * }\n * }\n *\n * // Flush any remaining content when stream ends\n * const remaining = buffer.flush()\n * if (remaining) {\n * await sendToClient(remaining)\n * }\n * ```\n */\n\nexport interface DirectiveStreamBuffer {\n /**\n * Process incoming text chunk and return text safe to stream.\n * Directives are buffered and stripped from output.\n *\n * @param chunk - Incoming text chunk\n * @returns Text safe to stream (directives removed)\n */\n process(chunk: string): string\n\n /**\n * Flush any remaining buffered content.\n * Call this when streaming is complete.\n *\n * @returns Any remaining text (with complete directives stripped)\n */\n flush(): string\n\n /**\n * Reset the buffer to initial state.\n */\n reset(): void\n}\n\n/**\n * Parsed directive information passed to the processor callback.\n */\nexport interface ParsedDirective {\n name: string\n attributes: Record<string, string>\n children: string\n}\n\n/**\n * Callback function for processing a complete directive.\n * Returns the text to insert in place of the directive syntax.\n */\nexport type DirectiveProcessor = (directive: ParsedDirective) => Promise<string>\n\n/**\n * A streaming processor that transforms directives using a callback.\n * Unlike DirectiveStreamBuffer which strips directives, this processes them.\n */\nexport interface DirectiveStreamProcessor {\n /**\n * Process incoming text chunk and return text with directives transformed.\n * Complete directives are passed to the processor callback.\n *\n * @param chunk - Incoming text chunk\n * @returns Text with complete directives replaced by processor output\n */\n process(chunk: string): Promise<string>\n\n /**\n * Flush any remaining buffered content.\n * Call this when streaming is complete.\n *\n * @returns Any remaining text with directives processed\n */\n flush(): Promise<string>\n\n /**\n * Reset the processor to initial state.\n */\n reset(): void\n}\n\n/**\n * Creates a buffer for handling directive syntax during streaming.\n *\n * Directives have the pattern: `::name{prop=\"value\" prop2=\"value2\"}`\n *\n * During streaming, the buffer:\n * 1. Streams non-directive text immediately\n * 2. Buffers potential directive content until it can determine if it's complete\n * 3. Strips complete directives from output (they'll be rendered as blocks at the end)\n *\n * This approach is similar to the React implementation which filters incomplete\n * directives using pattern matching, but adapted for incremental streaming where\n * we can't \"unsend\" text that was already streamed.\n */\nexport function createDirectiveStreamBuffer(): DirectiveStreamBuffer {\n let buffer = ''\n\n function process(chunk: string): string {\n buffer += chunk\n return extractStreamableText()\n }\n\n function flush(): string {\n const remaining = buffer\n buffer = ''\n // Strip any complete directives from remaining text\n return stripCompleteDirectives(remaining)\n }\n\n function reset(): void {\n buffer = ''\n }\n\n function extractStreamableText(): string {\n let output = ''\n\n while (buffer.length > 0) {\n // Find potential directive start (colon that might become ::)\n const colonIndex = buffer.indexOf(':')\n\n if (colonIndex === -1) {\n // No colon found - safe to output everything\n output += buffer\n buffer = ''\n break\n }\n\n // Output text before the colon\n if (colonIndex > 0) {\n output += buffer.slice(0, colonIndex)\n buffer = buffer.slice(colonIndex)\n }\n\n // Now buffer starts with ':'\n // If buffer is just a single ':', keep it buffered - might become '::' with next chunk\n if (buffer === ':') {\n break\n }\n\n // Block directive (::name{...})\n if (buffer.startsWith('::')) {\n if (!looksLikeBlockDirectiveStart(buffer)) {\n output += '::'\n buffer = buffer.slice(2)\n continue\n }\n const blockMatch = buffer.match(BLOCK_DIRECTIVE)\n if (blockMatch) {\n buffer = buffer.slice(blockMatch[0].length)\n continue\n }\n if (isDefinitelyNotBlockDirective(buffer)) {\n output += buffer.slice(0, 2)\n buffer = buffer.slice(2)\n continue\n }\n break\n }\n\n // Inline directive (:name[content]{...})\n if (looksLikeInlineDirectiveStart(buffer)) {\n const inlineMatch = buffer.match(INLINE_DIRECTIVE)\n if (inlineMatch) {\n buffer = buffer.slice(inlineMatch[0].length)\n continue\n }\n if (isDefinitelyNotInlineDirective(buffer)) {\n output += ':'\n buffer = buffer.slice(1)\n continue\n }\n break\n }\n\n // Not a directive\n output += ':'\n buffer = buffer.slice(1)\n }\n\n return output\n }\n\n function stripCompleteDirectives(text: string): string {\n return text\n .replace(new RegExp(BLOCK_DIRECTIVE.source, 'g'), '')\n .replace(new RegExp(INLINE_DIRECTIVE.source, 'g'), '')\n }\n\n return {\n process,\n flush,\n reset,\n }\n}\n\n/**\n * Parses directive attributes from a string like: id=\"123\" type=\"post\"\n */\nfunction parseDirectiveAttributes(attrString: string): Record<string, string> {\n const attrs: Record<string, string> = {}\n for (const match of attrString.matchAll(/(\\w+)=\"([^\"]*)\"/g)) {\n attrs[match[1]] = match[2]\n }\n return attrs\n}\n\n// ============================================================================\n// Directive regex patterns\n// ============================================================================\n\n// Matches block directives: ::name{attrs} or ::name[label]{attrs}\n// Captures: (1) name, (2) label or undefined, (3) attribute string\nconst BLOCK_DIRECTIVE = /::([a-zA-Z]\\w*)(?:\\[([^\\]]*)\\])?\\{([^}]*)\\}/\n\n// Matches inline directives: :name[label]{attrs}\n// Captures: (1) name, (2) label, (3) attribute string\nconst INLINE_DIRECTIVE = /:([a-zA-Z]\\w*)\\[([^\\]]*)\\]\\{([^}]*)\\}/\n\n// ============================================================================\n// Streaming detection helpers\n// ============================================================================\n\nfunction looksLikeBlockDirectiveStart(text: string): boolean {\n if (!text.startsWith('::')) return false\n if (text.length === 2) return true\n return /[a-zA-Z[{]/.test(text[2])\n}\n\nfunction looksLikeInlineDirectiveStart(text: string): boolean {\n if (text.length === 1) return true\n return /[a-zA-Z]/.test(text[1])\n}\n\nfunction isDefinitelyNotBlockDirective(text: string): boolean {\n const nameMatch = text.match(/^::([a-zA-Z]\\w*)/)\n if (!nameMatch) return text.length > 2 && !/[a-zA-Z]/.test(text[2])\n return isDefinitelyNotDirectiveAfterName(text.slice(nameMatch[0].length), false)\n}\n\nfunction isDefinitelyNotInlineDirective(text: string): boolean {\n const nameMatch = text.match(/^:([a-zA-Z]\\w*)/)\n if (!nameMatch) return text.length > 1 && !/[a-zA-Z]/.test(text[1])\n return isDefinitelyNotDirectiveAfterName(text.slice(nameMatch[0].length), true)\n}\n\n/**\n * Checks whether text after a directive name can still form a valid directive.\n * Block directives accept `{` (attrs) or `[` (label). Inline requires `[`.\n */\nfunction isDefinitelyNotDirectiveAfterName(afterName: string, requireBrackets: boolean): boolean {\n if (afterName.length === 0) return false\n if (afterName[0] === '{') return requireBrackets\n if (afterName[0] === '[') {\n const bracketEnd = afterName.indexOf(']')\n if (bracketEnd === -1) return false\n const afterBracket = afterName.slice(bracketEnd + 1)\n if (afterBracket.length === 0) return false\n return afterBracket[0] !== '{'\n }\n return true\n}\n\n/**\n * Creates a streaming processor that transforms directives using a callback.\n *\n * Unlike `createDirectiveStreamBuffer` which strips directives from output,\n * this processor passes complete directives to a callback and replaces them\n * with the callback's return value. This enables clients to render directives\n * as links, cards, or other formats during streaming.\n *\n * @example\n * ```ts\n * import { createDirectiveStreamProcessor } from '@sanity/agent-directives/streaming'\n *\n * // Slack client example\n * const processor = createDirectiveStreamProcessor(async ({ name, attributes }) => {\n * if (name === 'document') {\n * const doc = await fetchDocument(attributes.id)\n * return `[${doc.title}](${doc.url})`\n * }\n * return `[${name}]`\n * })\n *\n * for await (const chunk of llmStream) {\n * const text = await processor.process(chunk)\n * if (text) {\n * await streamToClient(text)\n * }\n * }\n *\n * const remaining = await processor.flush()\n * if (remaining) {\n * await streamToClient(remaining)\n * }\n * ```\n */\nexport function createDirectiveStreamProcessor(\n processDirective: DirectiveProcessor,\n): DirectiveStreamProcessor {\n let buffer = ''\n\n async function process(chunk: string): Promise<string> {\n buffer += chunk\n let output = ''\n\n while (buffer.length > 0) {\n const colonIndex = buffer.indexOf(':')\n\n if (colonIndex === -1) {\n output += buffer\n buffer = ''\n break\n }\n\n if (colonIndex > 0) {\n output += buffer.slice(0, colonIndex)\n buffer = buffer.slice(colonIndex)\n }\n\n if (buffer === ':') break\n\n // Block directive (::name{...})\n if (buffer.startsWith('::')) {\n if (!looksLikeBlockDirectiveStart(buffer)) {\n output += '::'\n buffer = buffer.slice(2)\n continue\n }\n const blockMatch = buffer.match(BLOCK_DIRECTIVE)\n if (blockMatch) {\n const [fullMatch, name, children, attrString] = blockMatch\n const attributes = parseDirectiveAttributes(attrString)\n output += await processDirective({ name, attributes, children: children ?? '' })\n buffer = buffer.slice(fullMatch.length)\n continue\n }\n if (isDefinitelyNotBlockDirective(buffer)) {\n output += buffer.slice(0, 2)\n buffer = buffer.slice(2)\n continue\n }\n break\n }\n\n // Inline directive (:name[content]{...})\n if (looksLikeInlineDirectiveStart(buffer)) {\n const inlineMatch = buffer.match(INLINE_DIRECTIVE)\n if (inlineMatch) {\n const [fullMatch, name, children, attrString] = inlineMatch\n const attributes = parseDirectiveAttributes(attrString)\n output += await processDirective({ name, attributes, children })\n buffer = buffer.slice(fullMatch.length)\n continue\n }\n if (isDefinitelyNotInlineDirective(buffer)) {\n output += ':'\n buffer = buffer.slice(1)\n continue\n }\n break\n }\n\n // Not a directive\n output += ':'\n buffer = buffer.slice(1)\n }\n\n return output\n }\n\n async function flush(): Promise<string> {\n const remaining = buffer\n buffer = ''\n let output = ''\n let text = remaining\n\n while (text.length > 0) {\n const inlineMatch = text.match(INLINE_DIRECTIVE)\n const blockMatch = text.match(BLOCK_DIRECTIVE)\n\n const inlineIdx = inlineMatch?.index ?? Infinity\n const blockIdx = blockMatch?.index ?? Infinity\n\n if (inlineIdx === Infinity && blockIdx === Infinity) {\n output += text\n break\n }\n\n if (inlineIdx <= blockIdx && inlineMatch) {\n output += text.slice(0, inlineMatch.index)\n const attributes = parseDirectiveAttributes(inlineMatch[3])\n output += await processDirective({\n name: inlineMatch[1],\n attributes,\n children: inlineMatch[2],\n })\n text = text.slice(inlineMatch.index! + inlineMatch[0].length)\n } else if (blockMatch) {\n output += text.slice(0, blockMatch.index)\n const attributes = parseDirectiveAttributes(blockMatch[3])\n output += await processDirective({\n name: blockMatch[1],\n attributes,\n children: blockMatch[2] ?? '',\n })\n text = text.slice(blockMatch.index! + blockMatch[0].length)\n }\n }\n\n return output\n }\n\n function reset(): void {\n buffer = ''\n }\n\n return {\n process,\n flush,\n reset,\n }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"streaming.js","sources":["../../src/lib/streaming.ts"],"sourcesContent":["import { BLOCK_DIRECTIVE, INLINE_DIRECTIVE, parseDirectiveAttributes } from '../_internal'\n\n/**\n * Streaming utilities for handling directive syntax during real-time text streaming.\n *\n * When streaming text that contains directives (e.g., `::document{id=\"123\"}`),\n * we need to buffer potential directive content and strip complete directives\n * from the streamed output, since they'll be rendered as blocks/components\n * at the end of the stream.\n *\n * @example\n * ```ts\n * import { createDirectiveStreamBuffer } from '@sanity/agent-directives/streaming'\n *\n * const buffer = createDirectiveStreamBuffer()\n *\n * for await (const chunk of stream) {\n * const textToStream = buffer.process(chunk)\n * if (textToStream) {\n * await sendToClient(textToStream)\n * }\n * }\n *\n * // Flush any remaining content when stream ends\n * const remaining = buffer.flush()\n * if (remaining) {\n * await sendToClient(remaining)\n * }\n * ```\n */\n\nexport interface DirectiveStreamBuffer {\n /**\n * Process incoming text chunk and return text safe to stream.\n * Directives are buffered and stripped from output.\n *\n * @param chunk - Incoming text chunk\n * @returns Text safe to stream (directives removed)\n */\n process(chunk: string): string\n\n /**\n * Flush any remaining buffered content.\n * Call this when streaming is complete.\n *\n * @returns Any remaining text (with complete directives stripped)\n */\n flush(): string\n\n /**\n * Reset the buffer to initial state.\n */\n reset(): void\n}\n\n/**\n * Parsed directive information passed to the processor callback.\n */\nexport interface ParsedDirective {\n name: string\n attributes: Record<string, string>\n children: string\n}\n\n/**\n * Callback function for processing a complete directive.\n * Returns the text to insert in place of the directive syntax.\n */\nexport type DirectiveProcessor = (directive: ParsedDirective) => Promise<string>\n\n/**\n * A streaming processor that transforms directives using a callback.\n * Unlike DirectiveStreamBuffer which strips directives, this processes them.\n */\nexport interface DirectiveStreamProcessor {\n /**\n * Process incoming text chunk and return text with directives transformed.\n * Complete directives are passed to the processor callback.\n *\n * @param chunk - Incoming text chunk\n * @returns Text with complete directives replaced by processor output\n */\n process(chunk: string): Promise<string>\n\n /**\n * Flush any remaining buffered content.\n * Call this when streaming is complete.\n *\n * @returns Any remaining text with directives processed\n */\n flush(): Promise<string>\n\n /**\n * Reset the processor to initial state.\n */\n reset(): void\n}\n\n/**\n * Creates a buffer for handling directive syntax during streaming.\n *\n * Directives have the pattern: `::name{prop=\"value\" prop2=\"value2\"}`\n *\n * During streaming, the buffer:\n * 1. Streams non-directive text immediately\n * 2. Buffers potential directive content until it can determine if it's complete\n * 3. Strips complete directives from output (they'll be rendered as blocks at the end)\n *\n * This approach is similar to the React implementation which filters incomplete\n * directives using pattern matching, but adapted for incremental streaming where\n * we can't \"unsend\" text that was already streamed.\n */\nexport function createDirectiveStreamBuffer(): DirectiveStreamBuffer {\n let buffer = ''\n\n function process(chunk: string): string {\n buffer += chunk\n return extractStreamableText()\n }\n\n function flush(): string {\n const remaining = buffer\n buffer = ''\n // Strip any complete directives from remaining text\n return stripCompleteDirectives(remaining)\n }\n\n function reset(): void {\n buffer = ''\n }\n\n function extractStreamableText(): string {\n let output = ''\n\n while (buffer.length > 0) {\n // Find potential directive start (colon that might become ::)\n const colonIndex = buffer.indexOf(':')\n\n if (colonIndex === -1) {\n // No colon found - safe to output everything\n output += buffer\n buffer = ''\n break\n }\n\n // Output text before the colon\n if (colonIndex > 0) {\n output += buffer.slice(0, colonIndex)\n buffer = buffer.slice(colonIndex)\n }\n\n // Now buffer starts with ':'\n // If buffer is just a single ':', keep it buffered - might become '::' with next chunk\n if (buffer === ':') {\n break\n }\n\n // Block directive (::name{...})\n if (buffer.startsWith('::')) {\n if (!looksLikeBlockDirectiveStart(buffer)) {\n output += '::'\n buffer = buffer.slice(2)\n continue\n }\n const blockMatch = buffer.match(BLOCK_DIRECTIVE)\n if (blockMatch) {\n buffer = buffer.slice(blockMatch[0].length)\n continue\n }\n if (isDefinitelyNotBlockDirective(buffer)) {\n output += buffer.slice(0, 2)\n buffer = buffer.slice(2)\n continue\n }\n break\n }\n\n // Inline directive (:name[content]{...})\n if (looksLikeInlineDirectiveStart(buffer)) {\n const inlineMatch = buffer.match(INLINE_DIRECTIVE)\n if (inlineMatch) {\n buffer = buffer.slice(inlineMatch[0].length)\n continue\n }\n if (isDefinitelyNotInlineDirective(buffer)) {\n output += ':'\n buffer = buffer.slice(1)\n continue\n }\n break\n }\n\n // Not a directive\n output += ':'\n buffer = buffer.slice(1)\n }\n\n return output\n }\n\n function stripCompleteDirectives(text: string): string {\n return text\n .replace(new RegExp(BLOCK_DIRECTIVE.source, 'g'), '')\n .replace(new RegExp(INLINE_DIRECTIVE.source, 'g'), '')\n }\n\n return {\n process,\n flush,\n reset,\n }\n}\n\n// ============================================================================\n// Streaming detection helpers\n// ============================================================================\n\nfunction looksLikeBlockDirectiveStart(text: string): boolean {\n if (!text.startsWith('::')) return false\n if (text.length === 2) return true\n return /[a-zA-Z[{]/.test(text[2])\n}\n\nfunction looksLikeInlineDirectiveStart(text: string): boolean {\n if (text.length === 1) return true\n return /[a-zA-Z]/.test(text[1])\n}\n\nfunction isDefinitelyNotBlockDirective(text: string): boolean {\n const nameMatch = text.match(/^::([a-zA-Z]\\w*)/)\n if (!nameMatch) return text.length > 2 && !/[a-zA-Z]/.test(text[2])\n return isDefinitelyNotDirectiveAfterName(text.slice(nameMatch[0].length), false)\n}\n\nfunction isDefinitelyNotInlineDirective(text: string): boolean {\n const nameMatch = text.match(/^:([a-zA-Z]\\w*)/)\n if (!nameMatch) return text.length > 1 && !/[a-zA-Z]/.test(text[1])\n return isDefinitelyNotDirectiveAfterName(text.slice(nameMatch[0].length), true)\n}\n\n/**\n * Checks whether text after a directive name can still form a valid directive.\n * Block directives accept `{` (attrs) or `[` (label). Inline requires `[`.\n */\nfunction isDefinitelyNotDirectiveAfterName(afterName: string, requireBrackets: boolean): boolean {\n if (afterName.length === 0) return false\n if (afterName[0] === '{') return requireBrackets\n if (afterName[0] === '[') {\n const bracketEnd = afterName.indexOf(']')\n if (bracketEnd === -1) return false\n const afterBracket = afterName.slice(bracketEnd + 1)\n if (afterBracket.length === 0) return false\n return afterBracket[0] !== '{'\n }\n return true\n}\n\n/**\n * Creates a streaming processor that transforms directives using a callback.\n *\n * Unlike `createDirectiveStreamBuffer` which strips directives from output,\n * this processor passes complete directives to a callback and replaces them\n * with the callback's return value. This enables clients to render directives\n * as links, cards, or other formats during streaming.\n *\n * @example\n * ```ts\n * import { createDirectiveStreamProcessor } from '@sanity/agent-directives/streaming'\n *\n * // Slack client example\n * const processor = createDirectiveStreamProcessor(async ({ name, attributes }) => {\n * if (name === 'document') {\n * const doc = await fetchDocument(attributes.id)\n * return `[${doc.title}](${doc.url})`\n * }\n * return `[${name}]`\n * })\n *\n * for await (const chunk of llmStream) {\n * const text = await processor.process(chunk)\n * if (text) {\n * await streamToClient(text)\n * }\n * }\n *\n * const remaining = await processor.flush()\n * if (remaining) {\n * await streamToClient(remaining)\n * }\n * ```\n */\nexport function createDirectiveStreamProcessor(\n processDirective: DirectiveProcessor,\n): DirectiveStreamProcessor {\n let buffer = ''\n\n async function process(chunk: string): Promise<string> {\n buffer += chunk\n let output = ''\n\n while (buffer.length > 0) {\n const colonIndex = buffer.indexOf(':')\n\n if (colonIndex === -1) {\n output += buffer\n buffer = ''\n break\n }\n\n if (colonIndex > 0) {\n output += buffer.slice(0, colonIndex)\n buffer = buffer.slice(colonIndex)\n }\n\n if (buffer === ':') break\n\n // Block directive (::name{...})\n if (buffer.startsWith('::')) {\n if (!looksLikeBlockDirectiveStart(buffer)) {\n output += '::'\n buffer = buffer.slice(2)\n continue\n }\n const blockMatch = buffer.match(BLOCK_DIRECTIVE)\n if (blockMatch) {\n const [fullMatch, name, children, attrString] = blockMatch\n const attributes = parseDirectiveAttributes(attrString)\n output += await processDirective({ name, attributes, children: children ?? '' })\n buffer = buffer.slice(fullMatch.length)\n continue\n }\n if (isDefinitelyNotBlockDirective(buffer)) {\n output += buffer.slice(0, 2)\n buffer = buffer.slice(2)\n continue\n }\n break\n }\n\n // Inline directive (:name[content]{...})\n if (looksLikeInlineDirectiveStart(buffer)) {\n const inlineMatch = buffer.match(INLINE_DIRECTIVE)\n if (inlineMatch) {\n const [fullMatch, name, children, attrString] = inlineMatch\n const attributes = parseDirectiveAttributes(attrString)\n output += await processDirective({ name, attributes, children })\n buffer = buffer.slice(fullMatch.length)\n continue\n }\n if (isDefinitelyNotInlineDirective(buffer)) {\n output += ':'\n buffer = buffer.slice(1)\n continue\n }\n break\n }\n\n // Not a directive\n output += ':'\n buffer = buffer.slice(1)\n }\n\n return output\n }\n\n async function flush(): Promise<string> {\n const remaining = buffer\n buffer = ''\n let output = ''\n let text = remaining\n\n while (text.length > 0) {\n const inlineMatch = text.match(INLINE_DIRECTIVE)\n const blockMatch = text.match(BLOCK_DIRECTIVE)\n\n const inlineIdx = inlineMatch?.index ?? Infinity\n const blockIdx = blockMatch?.index ?? Infinity\n\n if (inlineIdx === Infinity && blockIdx === Infinity) {\n output += text\n break\n }\n\n if (inlineIdx <= blockIdx && inlineMatch) {\n output += text.slice(0, inlineMatch.index)\n const attributes = parseDirectiveAttributes(inlineMatch[3])\n output += await processDirective({\n name: inlineMatch[1],\n attributes,\n children: inlineMatch[2],\n })\n text = text.slice(inlineMatch.index! + inlineMatch[0].length)\n } else if (blockMatch) {\n output += text.slice(0, blockMatch.index)\n const attributes = parseDirectiveAttributes(blockMatch[3])\n output += await processDirective({\n name: blockMatch[1],\n attributes,\n children: blockMatch[2] ?? '',\n })\n text = text.slice(blockMatch.index! + blockMatch[0].length)\n }\n }\n\n return output\n }\n\n function reset(): void {\n buffer = ''\n }\n\n return {\n process,\n flush,\n reset,\n }\n}\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanity/agent-directives",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.11",
|
|
4
4
|
"description": "Shared directive system for Sanity Agent",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sanity",
|
|
@@ -60,8 +60,8 @@
|
|
|
60
60
|
"react-markdown": "^10.0.0",
|
|
61
61
|
"typescript": "^5.9.3",
|
|
62
62
|
"vitest": "^4.0.18",
|
|
63
|
-
"@repo/
|
|
64
|
-
"@repo/
|
|
63
|
+
"@repo/package.config": "0.0.0",
|
|
64
|
+
"@repo/tsconfig": "0.0.0"
|
|
65
65
|
},
|
|
66
66
|
"peerDependencies": {
|
|
67
67
|
"react": "^18.0.0 || ^19.0.0"
|