import { AndroidAppLaunchSimulation } from '@igpapi/android';
import { Effect } from 'effect';
const login = Effect.gen(function* () {
// A fresh install's result is the login home alone: no `is` check.
const home = yield* AndroidAppLaunchSimulation({ seed: username });
const firstScreen = yield* home.logIn({ username, password });
// Only the handle's data leaves the scope: the handle itself must not be
// used after the scope that launched the process closes.
return firstScreen.id === 'FeedTimeline' ? firstScreen.items.map(entry => entry.type) : [];
});
const feedEntries = await Effect.runPromise(Effect.scoped(login));
The Android app launch: one call creates the client and launches the app,
and returns the first screen the app shows — the login home of a fresh
install, or the feed of a process that holds the stored user. The input picks
the result by type, so the caller pays for neither a dead LoginHomepageScreen.is
check on a stored session nor a branch on a splash a launch never brings.
There is no separate client creation and no separate launch step: the process
starts and is launched here.
The process lives in the Effect scope it was launched in, and its handle
must not be used after that scope closes. The layer, the native transport
included, is built in that scope, and the scope is the process's declared
lifetime. The library cannot prevent a handle from being used after the
scope today: the transport has no close, so the scope closes nothing, and
TypeScript cannot tie a handle's use to a scope. Enforcing it is the named
task process-lifetime-enforced-by-transport-close — a close on the
transport's client and a layer that finalizes it with the scope. Keep every
handle inside the scope that launched the process and let only its data out.
A second launch of the same process is not expressible — another call is
another process. To continue the session in a later program, take the
screen's checkpoint() and launch again from it ({ checkpoint }), or
persist a snapshot from a program over AndroidApp and launch from that.
The returned handle is the screen: its actions (screen.logIn(…) on the
login home, screen.nextPage() on the feed) run with the process's own
services, so the caller does not provide the layer to them. A login on that
handle continues the same process through the recorded post-login launch and
returns the feed's handle; a
two-factor answer returns the approval screen's handle, and a last-resort
escalation the code-entry screen's. The launch itself sends what the
recordings show: the pre-login bootstrap and the login home without a
session, the recorded launch order and its first screen with a stored one.
The input picks the result by type. Without a snapshot or a checkpoint
the launch is a fresh install, which holds no user session: its result is the
login screen alone. A checkpoint the type still names — the value
screen.checkpoint() returns — picks its result by that screen
(CheckpointFirstScreen): a pre-session screen (the login home the
logged-out launch opens and every screen its edges reach) is a process
without a session, so the launch opens the login home; any other screen is a
signed-in process, so its warm launch starts on the launch declaration's
first screen, StoredSessionFirstScreen (the feed), with no login home
and no splash to narrow away. A snapshot's type does not say whether its
process held a session, and a checkpoint that crossed JSON storage has only
the wide ScreenId, so both are the honest union
AndroidAppLaunchFirstScreen — as is the widened input and the caller's own
optional saved-session value, which may be either at run time; the type must
not allow screen.logIn(…) on a feed handle. The runtime reads the same
fact: a checkpoint whose screen and session contradict each other — an
in-session screen whose client holds no session, or a pre-session screen
whose client holds one — is refused before the process is built, with
CheckpointWithoutSessionError or CheckpointWithSessionError,
and nothing is sent; it is never launched into a screen the type excludes.
The location splash exists only on a launch after an account-recovery login,
which is a screen action's path, never a launch's. Adding a screen to either
union is a breaking change of that type. seed with a defined snapshot is refused by the type: the
snapshot names the device, so the seed would be silently dropped.
import { AndroidAppLaunchSimulation } from '@igpapi/android';
import { Effect } from 'effect';
const login = Effect.gen(function* () {
// A fresh install's result is the login home alone: no `is` check.
const home = yield* AndroidAppLaunchSimulation({ seed: username });
const firstScreen = yield* home.logIn({ username, password });
// Only the handle's data leaves the scope: the handle itself must not be
// used after the scope that launched the process closes.
return firstScreen.id === 'FeedTimeline' ? firstScreen.items.map(entry => entry.type) : [];
});
const feedEntries = await Effect.runPromise(Effect.scoped(login));
The Android app launch: one call creates the client and launches the app,
and returns the first screen the app shows — the login home of a fresh
install, or the feed of a process that holds the stored user. The input picks
the result by type, so the caller pays for neither a dead LoginHomepageScreen.is
check on a stored session nor a branch on a splash a launch never brings.
There is no separate client creation and no separate launch step: the process
starts and is launched here.
The process lives in the Effect scope it was launched in, and its handle
must not be used after that scope closes. The layer, the native transport
included, is built in that scope, and the scope is the process's declared
lifetime. The library cannot prevent a handle from being used after the
scope today: the transport has no close, so the scope closes nothing, and
TypeScript cannot tie a handle's use to a scope. Enforcing it is the named
task process-lifetime-enforced-by-transport-close — a close on the
transport's client and a layer that finalizes it with the scope. Keep every
handle inside the scope that launched the process and let only its data out.
A second launch of the same process is not expressible — another call is
another process. To continue the session in a later program, take the
screen's checkpoint() and launch again from it ({ checkpoint }), or
persist a snapshot from a program over AndroidApp and launch from that.
The returned handle is the screen: its actions (screen.logIn(…) on the
login home, screen.nextPage() on the feed) run with the process's own
services, so the caller does not provide the layer to them. A login on that
handle continues the same process through the recorded post-login launch and
returns the feed's handle; a
two-factor answer returns the approval screen's handle, and a last-resort
escalation the code-entry screen's. The launch itself sends what the
recordings show: the pre-login bootstrap and the login home without a
session, the recorded launch order and its first screen with a stored one.
The input picks the result by type. Without a snapshot or a checkpoint
the launch is a fresh install, which holds no user session: its result is the
login screen alone. A checkpoint the type still names — the value
screen.checkpoint() returns — picks its result by that screen
(CheckpointFirstScreen): a pre-session screen (the login home the
logged-out launch opens and every screen its edges reach) is a process
without a session, so the launch opens the login home; any other screen is a
signed-in process, so its warm launch starts on the launch declaration's
first screen, StoredSessionFirstScreen (the feed), with no login home
and no splash to narrow away. A snapshot's type does not say whether its
process held a session, and a checkpoint that crossed JSON storage has only
the wide ScreenId, so both are the honest union
AndroidAppLaunchFirstScreen — as is the widened input and the caller's own
optional saved-session value, which may be either at run time; the type must
not allow screen.logIn(…) on a feed handle. The runtime reads the same
fact: a checkpoint whose screen and session contradict each other — an
in-session screen whose client holds no session, or a pre-session screen
whose client holds one — is refused before the process is built, with
CheckpointWithoutSessionError or CheckpointWithSessionError,
and nothing is sent; it is never launched into a screen the type excludes.
The location splash exists only on a launch after an account-recovery login,
which is a screen action's path, never a launch's. Adding a screen to either
union is a breaking change of that type. seed with a defined snapshot is refused by the type: the
snapshot names the device, so the seed would be silently dropped.
import { AndroidAppLaunchSimulation } from '@igpapi/android';
import { Effect } from 'effect';
const login = Effect.gen(function* () {
// A fresh install's result is the login home alone: no `is` check.
const home = yield* AndroidAppLaunchSimulation({ seed: username });
const firstScreen = yield* home.logIn({ username, password });
// Only the handle's data leaves the scope: the handle itself must not be
// used after the scope that launched the process closes.
return firstScreen.id === 'FeedTimeline' ? firstScreen.items.map(entry => entry.type) : [];
});
const feedEntries = await Effect.runPromise(Effect.scoped(login));
The Android app launch: one call creates the client and launches the app,
and returns the first screen the app shows — the login home of a fresh
install, or the feed of a process that holds the stored user. The input picks
the result by type, so the caller pays for neither a dead LoginHomepageScreen.is
check on a stored session nor a branch on a splash a launch never brings.
There is no separate client creation and no separate launch step: the process
starts and is launched here.
The process lives in the Effect scope it was launched in, and its handle
must not be used after that scope closes. The layer, the native transport
included, is built in that scope, and the scope is the process's declared
lifetime. The library cannot prevent a handle from being used after the
scope today: the transport has no close, so the scope closes nothing, and
TypeScript cannot tie a handle's use to a scope. Enforcing it is the named
task process-lifetime-enforced-by-transport-close — a close on the
transport's client and a layer that finalizes it with the scope. Keep every
handle inside the scope that launched the process and let only its data out.
A second launch of the same process is not expressible — another call is
another process. To continue the session in a later program, take the
screen's checkpoint() and launch again from it ({ checkpoint }), or
persist a snapshot from a program over AndroidApp and launch from that.
The returned handle is the screen: its actions (screen.logIn(…) on the
login home, screen.nextPage() on the feed) run with the process's own
services, so the caller does not provide the layer to them. A login on that
handle continues the same process through the recorded post-login launch and
returns the feed's handle; a
two-factor answer returns the approval screen's handle, and a last-resort
escalation the code-entry screen's. The launch itself sends what the
recordings show: the pre-login bootstrap and the login home without a
session, the recorded launch order and its first screen with a stored one.
The input picks the result by type. Without a snapshot or a checkpoint
the launch is a fresh install, which holds no user session: its result is the
login screen alone. A checkpoint the type still names — the value
screen.checkpoint() returns — picks its result by that screen
(CheckpointFirstScreen): a pre-session screen (the login home the
logged-out launch opens and every screen its edges reach) is a process
without a session, so the launch opens the login home; any other screen is a
signed-in process, so its warm launch starts on the launch declaration's
first screen, StoredSessionFirstScreen (the feed), with no login home
and no splash to narrow away. A snapshot's type does not say whether its
process held a session, and a checkpoint that crossed JSON storage has only
the wide ScreenId, so both are the honest union
AndroidAppLaunchFirstScreen — as is the widened input and the caller's own
optional saved-session value, which may be either at run time; the type must
not allow screen.logIn(…) on a feed handle. The runtime reads the same
fact: a checkpoint whose screen and session contradict each other — an
in-session screen whose client holds no session, or a pre-session screen
whose client holds one — is refused before the process is built, with
CheckpointWithoutSessionError or CheckpointWithSessionError,
and nothing is sent; it is never launched into a screen the type excludes.
The location splash exists only on a launch after an account-recovery login,
which is a screen action's path, never a launch's. Adding a screen to either
union is a breaking change of that type. seed with a defined snapshot is refused by the type: the
snapshot names the device, so the seed would be silently dropped.
Optionalinput: AndroidAppLaunchFreshOptionsimport { AndroidAppLaunchSimulation } from '@igpapi/android';
import { Effect } from 'effect';
const login = Effect.gen(function* () {
// A fresh install's result is the login home alone: no `is` check.
const home = yield* AndroidAppLaunchSimulation({ seed: username });
const firstScreen = yield* home.logIn({ username, password });
// Only the handle's data leaves the scope: the handle itself must not be
// used after the scope that launched the process closes.
return firstScreen.id === 'FeedTimeline' ? firstScreen.items.map(entry => entry.type) : [];
});
const feedEntries = await Effect.runPromise(Effect.scoped(login));
The Android app launch: one call creates the client and launches the app,
and returns the first screen the app shows — the login home of a fresh
install, or the feed of a process that holds the stored user. The input picks
the result by type, so the caller pays for neither a dead LoginHomepageScreen.is
check on a stored session nor a branch on a splash a launch never brings.
There is no separate client creation and no separate launch step: the process
starts and is launched here.
The process lives in the Effect scope it was launched in, and its handle
must not be used after that scope closes. The layer, the native transport
included, is built in that scope, and the scope is the process's declared
lifetime. The library cannot prevent a handle from being used after the
scope today: the transport has no close, so the scope closes nothing, and
TypeScript cannot tie a handle's use to a scope. Enforcing it is the named
task process-lifetime-enforced-by-transport-close — a close on the
transport's client and a layer that finalizes it with the scope. Keep every
handle inside the scope that launched the process and let only its data out.
A second launch of the same process is not expressible — another call is
another process. To continue the session in a later program, take the
screen's checkpoint() and launch again from it ({ checkpoint }), or
persist a snapshot from a program over AndroidApp and launch from that.
The returned handle is the screen: its actions (screen.logIn(…) on the
login home, screen.nextPage() on the feed) run with the process's own
services, so the caller does not provide the layer to them. A login on that
handle continues the same process through the recorded post-login launch and
returns the feed's handle; a
two-factor answer returns the approval screen's handle, and a last-resort
escalation the code-entry screen's. The launch itself sends what the
recordings show: the pre-login bootstrap and the login home without a
session, the recorded launch order and its first screen with a stored one.
The input picks the result by type. Without a snapshot or a checkpoint
the launch is a fresh install, which holds no user session: its result is the
login screen alone. A checkpoint the type still names — the value
screen.checkpoint() returns — picks its result by that screen
(CheckpointFirstScreen): a pre-session screen (the login home the
logged-out launch opens and every screen its edges reach) is a process
without a session, so the launch opens the login home; any other screen is a
signed-in process, so its warm launch starts on the launch declaration's
first screen, StoredSessionFirstScreen (the feed), with no login home
and no splash to narrow away. A snapshot's type does not say whether its
process held a session, and a checkpoint that crossed JSON storage has only
the wide ScreenId, so both are the honest union
AndroidAppLaunchFirstScreen — as is the widened input and the caller's own
optional saved-session value, which may be either at run time; the type must
not allow screen.logIn(…) on a feed handle. The runtime reads the same
fact: a checkpoint whose screen and session contradict each other — an
in-session screen whose client holds no session, or a pre-session screen
whose client holds one — is refused before the process is built, with
CheckpointWithoutSessionError or CheckpointWithSessionError,
and nothing is sent; it is never launched into a screen the type excludes.
The location splash exists only on a launch after an account-recovery login,
which is a screen action's path, never a launch's. Adding a screen to either
union is a breaking change of that type. seed with a defined snapshot is refused by the type: the
snapshot names the device, so the seed would be silently dropped.
import { AndroidAppLaunchSimulation } from '@igpapi/android';
import { Effect } from 'effect';
const login = Effect.gen(function* () {
// A fresh install's result is the login home alone: no `is` check.
const home = yield* AndroidAppLaunchSimulation({ seed: username });
const firstScreen = yield* home.logIn({ username, password });
// Only the handle's data leaves the scope: the handle itself must not be
// used after the scope that launched the process closes.
return firstScreen.id === 'FeedTimeline' ? firstScreen.items.map(entry => entry.type) : [];
});
const feedEntries = await Effect.runPromise(Effect.scoped(login));
The Android app launch: one call creates the client and launches the app,
and returns the first screen the app shows — the login home of a fresh
install, or the feed of a process that holds the stored user. The input picks
the result by type, so the caller pays for neither a dead LoginHomepageScreen.is
check on a stored session nor a branch on a splash a launch never brings.
There is no separate client creation and no separate launch step: the process
starts and is launched here.
The process lives in the Effect scope it was launched in, and its handle
must not be used after that scope closes. The layer, the native transport
included, is built in that scope, and the scope is the process's declared
lifetime. The library cannot prevent a handle from being used after the
scope today: the transport has no close, so the scope closes nothing, and
TypeScript cannot tie a handle's use to a scope. Enforcing it is the named
task process-lifetime-enforced-by-transport-close — a close on the
transport's client and a layer that finalizes it with the scope. Keep every
handle inside the scope that launched the process and let only its data out.
A second launch of the same process is not expressible — another call is
another process. To continue the session in a later program, take the
screen's checkpoint() and launch again from it ({ checkpoint }), or
persist a snapshot from a program over AndroidApp and launch from that.
The returned handle is the screen: its actions (screen.logIn(…) on the
login home, screen.nextPage() on the feed) run with the process's own
services, so the caller does not provide the layer to them. A login on that
handle continues the same process through the recorded post-login launch and
returns the feed's handle; a
two-factor answer returns the approval screen's handle, and a last-resort
escalation the code-entry screen's. The launch itself sends what the
recordings show: the pre-login bootstrap and the login home without a
session, the recorded launch order and its first screen with a stored one.
The input picks the result by type. Without a snapshot or a checkpoint
the launch is a fresh install, which holds no user session: its result is the
login screen alone. A checkpoint the type still names — the value
screen.checkpoint() returns — picks its result by that screen
(CheckpointFirstScreen): a pre-session screen (the login home the
logged-out launch opens and every screen its edges reach) is a process
without a session, so the launch opens the login home; any other screen is a
signed-in process, so its warm launch starts on the launch declaration's
first screen, StoredSessionFirstScreen (the feed), with no login home
and no splash to narrow away. A snapshot's type does not say whether its
process held a session, and a checkpoint that crossed JSON storage has only
the wide ScreenId, so both are the honest union
AndroidAppLaunchFirstScreen — as is the widened input and the caller's own
optional saved-session value, which may be either at run time; the type must
not allow screen.logIn(…) on a feed handle. The runtime reads the same
fact: a checkpoint whose screen and session contradict each other — an
in-session screen whose client holds no session, or a pre-session screen
whose client holds one — is refused before the process is built, with
CheckpointWithoutSessionError or CheckpointWithSessionError,
and nothing is sent; it is never launched into a screen the type excludes.
The location splash exists only on a launch after an account-recovery login,
which is a screen action's path, never a launch's. Adding a screen to either
union is a breaking change of that type. seed with a defined snapshot is refused by the type: the
snapshot names the device, so the seed would be silently dropped.
import { AndroidAppLaunchSimulation } from '@igpapi/android';
import { Effect } from 'effect';
const login = Effect.gen(function* () {
// A fresh install's result is the login home alone: no `is` check.
const home = yield* AndroidAppLaunchSimulation({ seed: username });
const firstScreen = yield* home.logIn({ username, password });
// Only the handle's data leaves the scope: the handle itself must not be
// used after the scope that launched the process closes.
return firstScreen.id === 'FeedTimeline' ? firstScreen.items.map(entry => entry.type) : [];
});
const feedEntries = await Effect.runPromise(Effect.scoped(login));
The Android app launch: one call creates the client and launches the app, and returns the first screen the app shows — the login home of a fresh install, or the feed of a process that holds the stored user. The input picks the result by type, so the caller pays for neither a dead
LoginHomepageScreen.ischeck on a stored session nor a branch on a splash a launch never brings. There is no separate client creation and no separate launch step: the process starts and is launched here.The process lives in the Effect scope it was launched in, and its handle must not be used after that scope closes. The layer, the native transport included, is built in that scope, and the scope is the process's declared lifetime. The library cannot prevent a handle from being used after the scope today: the transport has no
close, so the scope closes nothing, and TypeScript cannot tie a handle's use to a scope. Enforcing it is the named taskprocess-lifetime-enforced-by-transport-close— acloseon the transport's client and a layer that finalizes it with the scope. Keep every handle inside the scope that launched the process and let only its data out. A second launch of the same process is not expressible — another call is another process. To continue the session in a later program, take the screen'scheckpoint()and launch again from it ({ checkpoint }), or persist a snapshot from a program overAndroidAppand launch from that.The returned handle is the screen: its actions (
screen.logIn(…)on the login home,screen.nextPage()on the feed) run with the process's own services, so the caller does not provide the layer to them. A login on that handle continues the same process through the recorded post-login launch and returns the feed's handle; a two-factor answer returns the approval screen's handle, and a last-resort escalation the code-entry screen's. The launch itself sends what the recordings show: the pre-login bootstrap and the login home without a session, the recorded launch order and its first screen with a stored one.The input picks the result by type. Without a snapshot or a checkpoint the launch is a fresh install, which holds no user session: its result is the login screen alone. A checkpoint the type still names — the value
screen.checkpoint()returns — picks its result by that screen (CheckpointFirstScreen): a pre-session screen (the login home the logged-out launch opens and every screen its edges reach) is a process without a session, so the launch opens the login home; any other screen is a signed-in process, so its warm launch starts on the launch declaration's first screen, StoredSessionFirstScreen (the feed), with no login home and no splash to narrow away. A snapshot's type does not say whether its process held a session, and a checkpoint that crossed JSON storage has only the wideScreenId, so both are the honest unionAndroidAppLaunchFirstScreen— as is the widened input and the caller's own optional saved-session value, which may be either at run time; the type must not allowscreen.logIn(…)on a feed handle. The runtime reads the same fact: a checkpoint whose screen and session contradict each other — an in-session screen whose client holds no session, or a pre-session screen whose client holds one — is refused before the process is built, with CheckpointWithoutSessionError or CheckpointWithSessionError, and nothing is sent; it is never launched into a screen the type excludes. The location splash exists only on a launch after an account-recovery login, which is a screen action's path, never a launch's. Adding a screen to either union is a breaking change of that type.seedwith a definedsnapshotis refused by the type: the snapshot names the device, so the seed would be silently dropped.