diff --git a/core/ts/core/Decorator.ts b/core/ts/core/Decorator.ts index 66856937..d4c6f7e7 100644 --- a/core/ts/core/Decorator.ts +++ b/core/ts/core/Decorator.ts @@ -24,6 +24,15 @@ export const Decorator = { getOpaqueData(name) { return opaqueData[name]; }, + processOutputModel({privateData}) { + if (privateData) { + const {__privateKey: key, ...privy} = privateData; + if (key) { + const data = Object.values(opaqueData).pop(); + data[key]['privateData'] = privy; + } + } + }, maybeDecorateModel(model, particle) { if (model && !Array.isArray(model)) { // for each item in model, regardless of key @@ -73,7 +82,7 @@ const maybeDecorate = (models, decorator, particle) => { // we don't want the decorator to have access to mutable globals const immutableState = Object.freeze(deepCopy(state)); // models become decorous - models = models.map(model => { + models = models.map((model, i) => { // use previously mutated data or initialize // TODO(cromwellian): I'd like to do Object.freeze() here, also somehow not mutate the models inplace // Possibly have setOpaqueData wrap the data so the privateData lives on the wrapper + internal immutable data @@ -82,7 +91,7 @@ const maybeDecorate = (models, decorator, particle) => { const immutableModel = Object.freeze(deepCopy(model)); const decorated = decorator(immutableModel, immutableInputs, immutableState); // set new privateData from returned - model.privateData = decorated.privateData; + model.privateData = {...decorated.privateData, __privateKey: i}; return { ...decorated, ...model, }; }); // sort (possible that all values undefined) diff --git a/core/ts/core/Host.ts b/core/ts/core/Host.ts index 5f2f3f30..60fc8f9e 100644 --- a/core/ts/core/Host.ts +++ b/core/ts/core/Host.ts @@ -86,6 +86,7 @@ export class Host extends EventEmitter { } protected output(outputModel, renderModel) { if (outputModel) { + Decorator.processOutputModel(outputModel); this.lastOutput = outputModel; this.arc?.assignOutputs(this, outputModel); } diff --git a/pkg/Library/Core/arcs.js b/pkg/Library/Core/arcs.js index 808ec29e..46b9534e 100644 --- a/pkg/Library/Core/arcs.js +++ b/pkg/Library/Core/arcs.js @@ -597,6 +597,16 @@ var Decorator = { getOpaqueData(name) { return opaqueData[name]; }, + processOutputModel({ privateData }) { + if (privateData) { + const key2 = privateData.__privateKey; + if (key2) { + const data = Object.values(opaqueData).pop(); + const { _privateKey, ...privy } = privateData; + data["privateData"] = privy; + } + } + }, maybeDecorateModel(model, particle) { if (model && !Array.isArray(model)) { values2(model).forEach((item) => { @@ -631,11 +641,11 @@ var maybeDecorate = (models, decorator, particle) => { if (decorator) { const immutableInputs = Object.freeze(deepCopy(inputs)); const immutableState = Object.freeze(deepCopy(state)); - models = models.map((model) => { + models = models.map((model, i) => { model.privateData = model.privateData || {}; const immutableModel = Object.freeze(deepCopy(model)); const decorated = decorator(immutableModel, immutableInputs, immutableState); - model.privateData = decorated.privateData; + model.privateData = { ...decorated.privateData, __privateKey: i }; return { ...decorated, ...model }; }); models.sort(sortByLc("sortKey")); @@ -732,6 +742,7 @@ var Host = class extends EventEmitter { } output(outputModel, renderModel) { if (outputModel) { + Decorator.processOutputModel(outputModel); this.lastOutput = outputModel; this.arc?.assignOutputs(this, outputModel); } diff --git a/pkg/Library/Core/arcs.js.map b/pkg/Library/Core/arcs.js.map index 3b08fa04..658d9de9 100644 --- a/pkg/Library/Core/arcs.js.map +++ b/pkg/Library/Core/arcs.js.map @@ -1,7 +1,7 @@ { "version": 3, "sources": ["../../../core/js/core/Particle.js", "../../../core/js/core/EventEmitter.js", "../../../core/js/utils/types.js", "../../../core/js/utils/log.js", "../../../core/js/core/Arc.js", "../../../core/js/utils/object.js", "../../../core/js/utils/rand.js", "../../../core/js/core/Decorator.js", "../../../core/js/core/Host.js", "../../../core/js/core/Store.js", "../../../core/js/utils/id.js", "../../../core/js/Runtime.js", "../../../core/js/recipe/RecipeParser.js", "../../../core/js/utils/matching.js", "../../../core/js/recipe/StoreCook.js", "../../../core/js/recipe/ParticleCook.js", "../../../core/js/recipe/Chef.js", "../../../core/js/utils/paths.js", "../../../core/js/isolation/code.js", "../../../core/js/isolation/vanilla.js", "../../../core/js/utils/utils.js", "../../../core/js/utils/date.js", "../../../core/js/utils/task.js", "../../../core/src/arcs.ts"], - "sourcesContent": ["/**\n * @license\n * Copyright 2022 Google LLC\n *\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file or at\n * https://developers.google.com/open-source/licenses/bsd\n */\n/*\n * PSA: code in this file is subject to isolation restrictions, including runtime processing.\n * Particle module interfaces with 3p code, and is often loaded into isolation contexts.\n**/\nconst { create, assign, keys, values, entries, defineProperty, setPrototypeOf } = Object;\nconst scope = globalThis['scope'] ?? {};\nconst { log, timeout } = scope;\nconst nob = () => create(null);\n// yay lambda, he gets a semi-colon ... named classes not so much\nconst storePrototype = new class {\n get empty() {\n return this.length === 0;\n }\n get data() {\n return this;\n }\n get pojo() {\n return this.data;\n }\n get json() {\n return JSON.stringify(this.pojo);\n }\n get pretty() {\n return JSON.stringify(this.pojo, null, ' ');\n }\n get keys() {\n return keys(this.data);\n }\n get length() {\n return keys(this.data).length;\n }\n get values() {\n return values(this.data);\n }\n get entries() {\n return entries(this.data);\n }\n set(key, value) {\n this.data[key] = value;\n }\n setByIndex(index, value) {\n this.data[this.keys[index]] = value;\n }\n add(...values) {\n values.forEach(value => this.data[scope.makeKey()] = value);\n }\n push(...values) {\n this.add(...values);\n }\n remove(value) {\n entries(this.data).find(([key, entry]) => {\n if (entry === value) {\n delete this.data[key];\n return true;\n }\n });\n }\n has(key) {\n return this.data[key] !== undefined;\n }\n get(key) {\n return this.getByKey(key);\n }\n getByKey(key) {\n return this.data[key];\n }\n getByIndex(index) {\n return this.data[this.keys[index]];\n }\n delete(key) {\n delete this.data[key];\n }\n deleteByIndex(index) {\n delete this.data[this.keys[index]];\n }\n assign(dictionary) {\n assign(this.data, dictionary);\n }\n map(mapFunc) {\n return this.values.map(mapFunc);\n }\n toString() {\n return this.pretty;\n }\n};\n/**\n * ParticleAPI functions are called at various points in the particle's lifecycle.\n * Developers should override these functions as needed to give a particle\n * functionality.\n */\nexport class ParticleApi {\n /**\n * Particles that render on a surface should provide a template. The template\n * can include double curly bracketed keys that will be interpolated at\n * runtime.\n *\n * To dynamically change the template, we double curly braced keys must be\n * the only thing inside a div or span:\n * ```\n * {{key}}.\n *
{{key}}
\n * ```\n *\n * The value for each key is returned from the {@link render | render method}.\n *\n * Double curly bracketed keys can also be placed inside div definitions to\n * change attributes. In this instance we place them inside quotation marks.\n * For example:\n * ```\n *