-
Notifications
You must be signed in to change notification settings - Fork 74
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
133 additions
and
72 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,75 @@ | ||
# Parameterizing Modules with Environment Options | ||
|
||
JavaScript module semantics resists attempts to parameterize a module's | ||
initialization behavior. A module initializes in order according to | ||
the path by which it is first imported, and then the initialized module | ||
is reused by all the other times it is imported. Compartments give us | ||
the opportunity to bind the same import name to different imported | ||
modules, depending on the package/compartment doing the import. Compartments | ||
also address the difficulty of parameterizing a module's initialization | ||
logic, but not in a pleasant manner. | ||
|
||
A pleasant parameterization would be for a static module to be function-like | ||
with explicit parameters, and for the parameterization to be like | ||
calling the static module with parameters in order to derive from it a | ||
module instance. Compartments instead lets us parameterize the meaning | ||
of a module instance derived from a static module according to the | ||
three namespaces provided by the JavaScript semantics, affecting the | ||
meaning of a module instance. | ||
* The global variable namespaces. | ||
* The global scope, aliased to properties of the global object. | ||
This is necessarily compartment-wide. In our | ||
recommened usage pattern of one compartment per package, | ||
each global would be package-wide. (See LavaMoat) | ||
* The global lexical scope. The SES-shim compartments support | ||
these both compartment-wide as well as per-module. But it is | ||
not yet clear what we will propose in the Compartment proposal. | ||
* The import namespace. | ||
* The host hooks. | ||
|
||
This `@endo/env-options` package follows the Node precedent for | ||
finding Unix environment variable settings: looking for a | ||
global `process` object holding an `env` object, | ||
optionally holding a property with the same name as the option, | ||
whose value is the configuration setting of that option. | ||
|
||
```js | ||
import { makeEnvironmentCaptor } from '@endo/env-options'; | ||
const { getEnvironmentOption } = makeEnvironmentCaptor(globalThis); | ||
const FooBarOption = getEnvironmentOption('FOO_BAR', 'absent'); | ||
``` | ||
|
||
The first argument to `getEnvironmentOption` is the name of the option. | ||
The value of `FooBarOption` would then be the value of | ||
`globalThis.process.env.FOO_BAR`, if present. | ||
If setting is either absent or `undefined`, the default `'absent'` | ||
would be used instead. | ||
|
||
In either case, reflecting Unix environment variable expectations, | ||
the resulting setting must be a string. | ||
This restriction also helps ensure that this channel is used only to pass data, | ||
not authority beyond the ability to read this global state. | ||
|
||
The `makeEnvironmentCaptor` function also returns a | ||
`getCapturedEnvironmentOptionNames` function for use to give feedback about | ||
which environment variables were actually read, for diagnostic purposes. The | ||
ses-shim `lockdown` contains code such as the following, to explain which | ||
environment variables were read to provide `lockdown` settings. | ||
|
||
```js | ||
import { makeEnvironmentCaptor } from '@endo/env-options'; | ||
const { | ||
getEnvironmentOption, | ||
getCapturedEnvironmentOptionNames, | ||
} = makeEnvironmentCaptor(globalThis); | ||
... | ||
const capturedEnvironmentOptionNames = getCapturedEnvironmentOptionNames(); | ||
if (capturedEnvironmentOptionNames.length > 0) { | ||
console.warn( | ||
`SES Lockdown using options from environment variables ${enJoin( | ||
arrayMap(capturedEnvironmentOptionNames, q), | ||
'and', | ||
)}`, | ||
); | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import { test } from './prepare-test-env-ava.js'; | ||
import { makeEnvironmentCaptor } from '../src/environment-options.js'; | ||
|
||
test('test env options empty env', async t => { | ||
const c1 = new Compartment(); | ||
const { getEnvironmentOption, getCapturedEnvironmentOptionNames } = | ||
makeEnvironmentCaptor(c1.globalThis); | ||
|
||
const c1foo = getEnvironmentOption('FOO', 'none'); | ||
t.is(c1foo, 'none'); | ||
t.deepEqual(getCapturedEnvironmentOptionNames(), []); | ||
}); | ||
|
||
test('test env options present', t => { | ||
const c2 = new Compartment({ | ||
process: { | ||
env: { | ||
FOO: 'bar', | ||
BAD: ['not a string'], | ||
}, | ||
}, | ||
}); | ||
const { getEnvironmentOption, getCapturedEnvironmentOptionNames } = | ||
makeEnvironmentCaptor(c2.globalThis); | ||
|
||
const c2foo = getEnvironmentOption('FOO', 'none'); | ||
t.is(c2foo, 'bar'); | ||
t.deepEqual(getCapturedEnvironmentOptionNames(), ['FOO']); | ||
|
||
t.throws(() => getEnvironmentOption('BAD', 'none'), { | ||
message: | ||
'Environment option named "BAD", if present, must have a corresponding string value, got ["not a string"]', | ||
}); | ||
t.throws(() => getEnvironmentOption('WORSE', ['none']), { | ||
message: 'Environment option default setting ["none"] must be a string.', | ||
}); | ||
}); |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters