{"_id":"@bradleymeck/compositekey","_rev":"1-e5fbca31829c85fb5cf43a8a65f055b6","name":"@bradleymeck/compositekey","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@bradleymeck/compositekey","version":"1.0.0","description":"This proposal seeks to add APIs to create composite keys while still allowing the components of the composite key to be GC'd.","main":"polyfill.js","scripts":{"test":"node test.js"},"keywords":[],"author":{"name":"bfarias@godaddy.com"},"license":"MIT","_id":"@bradleymeck/compositekey@1.0.0","_npmVersion":"5.3.0","_nodeVersion":"8.5.0","_npmUser":{"name":"bradleymeck","email":"bradley.meck@gmail.com"},"dist":{"integrity":"sha512-3Srz7f/4Kx2giwtP5aBsUFUkgzIL33MzurcNV4/8/jdoSksKhvaGIJPvxXPnL5+WVkwa3dsnNaAoQhDoJR/dgg==","shasum":"a2b6acd317cb0481a6bb632ec86c1ec363c36747","tarball":"https://registry.npmjs.org/@bradleymeck/compositekey/-/compositekey-1.0.0.tgz","fileCount":4,"unpackedSize":8151,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb1x0aCRA9TVsSAnZWagAAOcMQAJ0ik+EGr5cV35Htgu/S\nDoGSd8lxOheKKZd/kNts7SuCaY0Oi5LVTt1Ls8Dw6RwcdZBtlfcGEGkPJX7N\nyvZxOMd+SbWyBFlDl7pAwN4yfCxIHz9NX0S3Gk+bPC5RZ2g1tbkbE+K68zLY\nW/Kf97aeGkcztlk39C00BUoAPzJTg7Utm5s2QWgg9JRIETPLrQ1XqfCouvkS\ndU9TA31TuY/Lyfi5iWGcSbV5lr2dk3NsXI5AaJ44DuFOQDifRh5Iv4v6ttQ1\njSFLTeZqvwUNMCs0YWw50Iwn3hKEniP+UUL0ybUPp1VWL5hGdH2RrjEHsFP7\n+E9lYw5rR8FaLUdHSE8hzGq4JVZM2gxdVmkvjr6T0KQTsY7sEHrgfrBMLcrE\n6YcCE99Nyuw7bLHUIj+e4MExW8NlVGwa12Xh+otbt+T0QKfGE4GpUiL/Xu40\nrZhLkEKxoPq7TzN73J4FZavdYnrkzB6oAju/0EN0JLoCJ6NHbRqZTc8emApV\nkZ7KZPZZ+uQTNa41hhoYBM/aXn6mAl+PVX0aZYGIATJc0bYNYhjRFo99E4Gj\nkBCtNusL5wn6hhDx8kAQsLwY8JBErYgkWN4JaQV2EwxZuWRac/MzZQQJq+E9\nJZSWsSx2M9JUobl9nX6lFjzTFcm/0+q91XbUKOyY2A/xTP3xtgohpL/X+foY\nGyHj\r\n=2S7M\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGYY4o1H5GD9HXCrMPR+4Jp5p8ytJHXd15l06VCVbWXcAiAmvra/l2APTvspfpjOjQRAg+mI6QpNrPkRzFEKAB1mfQ=="}]},"maintainers":[{"name":"bradleymeck","email":"bradley.meck@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/compositekey_1.0.0_1540824345503_0.407431058846087"},"_hasShrinkwrap":false}},"time":{"created":"2018-10-29T14:45:45.349Z","1.0.0":"2018-10-29T14:45:45.595Z","modified":"2022-04-04T20:20:08.836Z"},"maintainers":[{"name":"bradleymeck","email":"bradley.meck@gmail.com"}],"description":"This proposal seeks to add APIs to create composite keys while still allowing the components of the composite key to be GC'd.","keywords":[],"author":{"name":"bfarias@godaddy.com"},"license":"MIT","readme":"# compositeKey, compositeSymbol\n\nThis proposal seeks to add APIs to create composite keys while still allowing the components of the composite key to be GC'd.\n\n## API\n\nIn all APIs order of arguments is preserved in the path to the key. `compositeKey(a, b)` is different from `compositeKey(b, a)`.\n\n`compositeKey` requires at least one component must be a valid key that can be placed in a `WeakMap` . This is because the main use case for `compositeKey` is to allow GC to occur when the lifetime of the components is ended. `compositeSymbol` is for strongly putting the key on an Object and does not benefit from this.\n\n```mjs\ncompositeKey(...parts: [...any]) : Object.freeze({__proto__:null})\n\ncompositeSymbol(...parts: [...any]) : Symbol()\n```\n\n## Where will it live\n\nA builtin module; how to import builtin modules TBD based upon current TC39 discussions.\n\n## FAQ\n\n### Why have both `compositeKey` and `compositeSymbol`?\n\nThey are serving two slightly different use cases.\n\n#### `compositeKey`\n\nAllows using a Map/Set/WeakMap to weakly and/or privately associate data with the lifetime of a group of values.\n\n#### `compositeSymbol`\n\nAllows strongly attaching data to an Object that is associtated with a group of values. This API can be roughly recreated by using:\n\n```mjs\nlet symbols = new WeakMap;\ncompositeSymbol = (...parts) => {\n  const key = compositeKey(...parts);\n  if (!symbols.has(key)) symbols.set(key, Symbol());\n  return symbols.get(key);\n}\n```\n\nHowever, this causes a problem of not being a global cache like `Symbol.for` or `compositeKey` and may cause fragmentation. It also would be ideal to have `compositeSymbol` act like `Symbol.for` in order to reduce total number of possible entries being held onto.\n\n### Why a frozen empty Object for `compositeKey`?\n\nSo that properties cannot be added to the object that will leak to the global or can be used as a public side channel.\n\nThis gives a few constraints:\n\n1. The return value must be frozen and a frozen prototype to prevent the side channel from being able to be obtained purely off the reference itself. This leads to `null` being a good choice for the prototype.\n\n2. This constraint must be applied to all properties of the object. While no properties are planned for the return value, the values of properties should follow these rules and/or be a primitive.\n\n### Why require a lifetime?\n\nThis prevents accidental leakage by always ensuring keys have a lifetime associated with them.\n\n### What scope is the idempotentcy?\n\nStill up for debate but some TC39 members would like it to be per Realm.\n\nHaving it be per Realm allows the key store to be more granular and free up segments as Realms are GC'd, but means that there could be multiple keys that correspond to `compositeKey(A, B)` if you obtain multiple `compositeKey` instances from multiple realms.\n\nHaving it be across Realms means that you cannot cause duplicate keys for component parts, and matches with `Symbol.for`. Since the result of `compositeKey` has a null prototype there is not a way to distinguish which Realm the result was first created in.\n\nCurrently, this proposal is looking to progress with cross Realm idempotentcy.\n\n### When can the key be GC'd\n\nThe path to a key in the key store can be GC'd once any owner of a lifetime in the path is GC'd. This means once any single non-primitive component is GC'd the key cannot be obtained again using these APIs. The key itself is an object subject to normal GC rules and will be removed when it is no longer strongly held.\n\nIf you only store keys in `WeakMap`s the key and associated values can be GC'd as soon a component of the key is GC'd.\n\nIf you store keys in strongly held structues like a `Map`; the key will not be able to be GC'd since the key could still be obtained inspecting `map.keys()` which could be used to get the value associated with the key.\n\n### How could I create a composite key that can return its components?\n\nYou can create a `Map` that strongly preserves your components in order: \n\n```mjs\nconst myValues = new Map();\n\nconst components = [a, b];\nconst myKey = compositeKey(...components);\nmyValues.set(myKey, components);\n\n\n// ...\n\nlet [a, b] = myValues.get(myKey);\n```\n","readmeFilename":"README.md"}