@@ -122,7 +122,7 @@ export class JQ extends Directive {
122122 rawValue : ( val : JQCompatible ) => ( { future_id : null , val } ) ,
123123 } ;
124124
125- override next ( ...items : TraceProp [ ] ) {
125+ override next ( ..._items : TraceProp [ ] ) {
126126 return new JQ ( this . query , this . target ) ;
127127 }
128128
@@ -189,6 +189,84 @@ export class StringConcat extends Directive {
189189 }
190190}
191191
192+ type JinjaTemplate =
193+ | {
194+ future_id : string ;
195+ val : null ;
196+ }
197+ | { val : string ; future_id : null } ;
198+
199+ export type JinjaVariables = {
200+ [ key : string ] :
201+ | string
202+ | number
203+ | boolean
204+ | ( string | number | boolean ) [ ]
205+ | JinjaVariables
206+ | Future < any > ;
207+ } ;
208+
209+ export class Jinja extends Directive {
210+ template : string | Future < string > ;
211+ variables : JinjaVariables ;
212+ items : Future < any > [ ] ;
213+
214+ static templateJSON ( template : string | Future < string > ) : JinjaTemplate {
215+ return template instanceof Future
216+ ? // @ts -ignore
217+ { val : null , future_id : template . _id }
218+ : { val : template , future_id : null } ;
219+ }
220+
221+ constructor ( template : string | Future < string > , variables : JinjaVariables ) {
222+ super ( ) ;
223+ this . template = template ;
224+ this . variables = variables ;
225+
226+ // use items to contain all of the futures from the inputs
227+ const futures = new Set < Future < any > > ( ) ;
228+ const collectFutures = ( obj : any ) => {
229+ if ( Array . isArray ( obj ) ) {
230+ for ( let item of obj ) {
231+ collectFutures ( item ) ;
232+ }
233+ }
234+
235+ if ( obj instanceof Future ) {
236+ futures . add ( obj ) ;
237+ return ;
238+ }
239+
240+ if ( obj && typeof obj === "object" ) {
241+ for ( let key of Object . keys ( obj ) ) {
242+ collectFutures ( obj [ key ] ) ;
243+ }
244+ }
245+ } ;
246+ collectFutures ( [ template , variables ] ) ;
247+ this . items = Array . from ( futures )
248+ }
249+
250+ override next ( ..._items : any [ ] ) {
251+ return new Jinja ( this . template , this . variables ) ;
252+ }
253+
254+ override async result ( ) : Promise < string > {
255+ return this . template instanceof Future
256+ ? // @ts -ignore
257+ await this . template . _result ( )
258+ : this . template ;
259+ }
260+
261+ override toJSON ( ) : any {
262+ return {
263+ type : "jinja" ,
264+ template : Jinja . templateJSON ( this . template ) ,
265+ variables : replaceWithPlaceholders ( this . variables ) ,
266+ } ;
267+ }
268+ }
269+
192270export abstract class Future < T > {
193271 protected _directive : Directive ;
194272 protected _id : string = "" ;
@@ -263,6 +341,13 @@ export class FutureString extends Future<string> {
263341 return FutureString . concat ( ...[ this , ...items ] ) ;
264342 }
265343
344+ static jinja (
345+ template : string | FutureString ,
346+ variables : JinjaVariables ,
347+ ) : FutureString {
348+ return new FutureString ( new Jinja ( template , variables ) ) ;
349+ }
350+
266351 protected override async _result ( ) : Promise < string > {
267352 return super . _result ( ) ;
268353 }
@@ -315,3 +400,27 @@ export class FutureAnyObject extends Future<Object> {
315400 return super . _result ( ) ;
316401 }
317402}
403+
404+ /**
405+ * @internal
406+ * Given some value, recursively replace `Future` instances with SB Placeholder
407+ */
408+ export const replaceWithPlaceholders = ( val : any ) : any => {
409+ if ( Array . isArray ( val ) ) {
410+ return val . map ( ( item ) => replaceWithPlaceholders ( item ) ) ;
411+ }
412+
413+ if ( val instanceof Future ) {
414+ // @ts -expect-error (accessing protected method toPlaceholder)
415+ return val . toPlaceholder ( ) ;
416+ }
417+
418+ if ( val && typeof val === "object" ) {
419+ return Object . keys ( val ) . reduce ( ( acc : any , k : any ) => {
420+ acc [ k ] = replaceWithPlaceholders ( val [ k ] ) ;
421+ return acc ;
422+ } , { } ) ;
423+ }
424+
425+ return val ;
426+ } ;
0 commit comments